【问题标题】:Unexpected Java Functional Interface conversion意外的 Java 功能接口转换
【发布时间】:2018-12-27 12:00:49
【问题描述】:

我有以下一段代码,它使用 java 功能接口,可以编译,但不清楚它为什么编译:

public class App {

    public static void main(String[] args) throws Exception {

        final RecordIterator it = new RecordIterator<MyRecord>();

        final UpdateManager updateManager = new UpdateManager();
        updateManager.doUpdateForEach(it, DatabaseOperator::updateInfo);

    }
}

class UpdateManager {

    public void doUpdateForEach(final RecordIterator recordIterator,
                                final FunctionalStuff<MyRecord> updateAction) throws Exception {

        updateAction.execute(new DatabaseOperator(), new MyRecord());

    }

}

class RecordIterator<E> {

}

@FunctionalInterface
interface FunctionalStuff<T> {

    void execute(final DatabaseOperator database, final T iterator) throws Exception;

}

class DatabaseOperator {

    public void updateInfo(final MyRecord r) {

    }

}

class MyRecord {

}

所以,我的困惑在于main 方法:

  • main方法的最后一行是updateManager.doUpdateForEach(it, DatabaseOperator::updateInfo);
  • UpdateManager#doUpdateForEach 方法需要 RecordIterator(好的,有道理)和 FunctionalStuff
  • FunctionalStuff 有一个方法(显然),它接收 2 个参数
  • doUpdateForEach 的第二个参数是方法引用 (DatabaseOperator::updateInfo)
  • DatabaseOperator::updateInfo 方法接收单个参数

这是如何编译的? DatabaseOperator::updateInfo 方法引用是如何转换成函数式接口的?我错过了一些明显的东西吗?或者是功能接口的一些极端情况?

【问题讨论】:

    标签: java java-8 functional-interface


    【解决方案1】:

    DatabaseOperator::updateInfo 方法引用是如何转换的 进入功能界面?

    您的方法引用的有效 lambda 表示是:

    updateManager.doUpdateForEach(it, (databaseOperator, r) -> databaseOperator.updateInfo(r));
    

    这是匿名类的进一步表示:

    new FunctionalStuff<MyRecord>() {
        @Override
        public void execute(DatabaseOperator databaseOperator, MyRecord r) throws Exception {
            databaseOperator.updateInfo(r);
        }
    });
    

    【讨论】:

      【解决方案2】:

      首先,FunctionalStuff&lt;T&gt; 是这样定义的:

      @FunctionalInterface
      interface FunctionalStuff<T> {
          void execute(final DatabaseOperator database, final T iterator) throws Exception;
      }
      

      方法引用DatabaseOperator::updateInfo 被转换为FunctionalStuff&lt;MyRecord&gt; 的一个实例(我留下了实际类型以供澄清,但它们可以省略):

      FunctionalStuff<MyRecord> func = (DatabaseOperator database, MyRecord r) -> database.updateInfo(r);
      

      或者,如果您想将其用作匿名类:

      FunctionalStuff<MyRecord> func = new FunctionalStuff<MyRecord>() {
          void execute(final DatabaseOperator database, final MyRecord r) {
              database.updateInfo(r);
          }
      }
      

      请参阅tutorial 并查看以下示例:

      对特定类型的任意对象的实例方法的引用

      下面是一个引用一个实例方法的例子 特定类型的任意对象:

      String[] stringArray = { "Barbara", "James", "Mary", "John",
          "Patricia", "Robert", "Michael", "Linda" };
      Arrays.sort(stringArray, String::compareToIgnoreCase);
      

      方法引用的等效 lambda 表达式 String::compareToIgnoreCase 会有形式参数列表 (String a, String b),其中ab 是任意名称,用于 更好地描述这个例子。方法引用将调用 方法a.compareToIgnoreCase(b).

      【讨论】:

        【解决方案3】:

        有 4 种不同类型的方法引用,您正在使用 Instance method reference of an object of a particular type

        现在乍一看,它类似于static method reference,即Class::staticType,但有以下区别:

        - the method should be present in the class same as type of first argument in functional interface
        - The method used should have one less argument as opposed to number of arguments in the method declared in functional interface as the **this** reference is taken as first argument.
        

        因此,在您的情况下,DatabaseOperator 类中存在方法 DatabaseOperator#updateInfo,这与函数接口内 execute 方法的第一个参数的类型 DatabaseOperator 以及方法中的参数数量相同少一个,因为 this 引用被作为第一个参数。

        如果您将 DatabaseOperator#updateInfo 更改为采用两个参数,则编译器将显示错误消息 Cannot make a static reference to the non-static method updateInfo from the type DatabaseOperator。因此,您可以将用作引用的方法设为静态,或者您必须使用 new DatabaseOperator()#updateInfo,这是 java 中的另外两种方法引用。

        【讨论】:

          【解决方案4】:

          这不是 lambda 的实际工作方式,但基本上你可以看到

          updateManager.doUpdateForEach(it, DatabaseOperator::updateInfo);
          

          作为

          DatabaseOperator referencedMethodOwner = instanceGivenAsMethodExpression;
          updateManager.doUpdateForEach(it, 
          
              new FunctionalStuff{
                  void execute(final T iterator) throws Exception{
                      referencedMethodOwner.updateInfo(iterator)
                 }
              });
          

          【讨论】:

          • 这根本不编译。
          • 这不应该被编译。
          • 它没有解释任何东西。投反对票,因为您的观点不清楚。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-06-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-04-01
          • 2015-12-15
          • 1970-01-01
          相关资源
          最近更新 更多