1

假设你有一个方法

void m(String s) {} 

Consumer通过使用新的方法引用,您可以从中创建一个

Consumer<String> c = Class::m;

如果您现在通过反射查看创建的 Consumer 的类,您会看到它唯一声明的方法是

void accept(Object)

如果通过创建匿名内部类以旧方式创建消费者

Consumer<String> c = new Consumer<String>() {
    public void accept(String s){}
}

会有桥接法void accept(Object)以及void accept(String).

现在假设我们必须传递这个消费者,从而失去它的泛型类型。如果你有一个实现Consumer<String>而不是 Lambda 表达式,你可以通过使用 relfection 来访问它的方法来取回它。由于通过方法引用创建的消费者只有通用方法,所以这不起作用。有没有办法获取方法引用创建的消费者的参数类型?

4

2 回答 2

3

抱歉,我之前没有看到这个问题。如果您在 Oracle 或 OpenJDK 上运行,则可以使用TypeTools从 lambda 或方法引用中解析泛型类型信息。前任:

Consumer<String> c = this::m;
Class<?> type = TypeResolver.resolveRawArgument(Consumer.class, c.getClass());
assert type == String.class;

很简单。告诉你的朋友 :)

于 2015-07-24T15:58:18.880 回答
1

调用原始消费者(没有泛型)总是会导致警告。您根本不应该这样做,因为您不知道类型。原始类型没有类型。Java 8 并没有改变这一点。

接口 Consumer 具有泛型类型,但您在运行时不知道。即使不是 lambdas。有些课程实际上可能有这些信息,但这对你没有帮助。如果您确实需要知道类型,那么只需创建这样一个接口。

  @FunctionalInterface
  public static interface StringConsumer {
    void consume(String s);
  }

  public static void main(String[] args) throws Throwable {
    StringConsumer sc = System.out::println;
    sc.consume("Hello");
    Method m = StringConsumer.class.getMethods()[0]; // only one: "consume"
    m.invoke(sc, m.getParameters()[0].getType().getSimpleName());
  }

这是一些示例代码和输出,以查看有哪些方法以及它们如何可以调用和不能调用:

package com.example.foo;

import static java.util.Arrays.asList;

import java.lang.reflect.Method;
import java.util.function.Consumer;

public class SomeClass {
  @FunctionalInterface
  static interface FI {
    public void m(String s);
  }

  static final class MyRegularClass {
    @SuppressWarnings("static-method")
    public void m(String s) {
      System.out.println("MyRegularClass: " + s);
    };
  }

  static final class MyGenericClass<T> {
    public void m(T s) {
      System.out.println("MyGenericClass: " + s);
    };
  }

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

    Consumer<String> c1 = (s) -> {
      System.out.println("Lambda: " + s);
    };
    Consumer<String> c2 = new Consumer<String>() {
      public void accept(String s) {
        System.out.println("Anonym: " + s);
      }
    };
    Consumer<String> c3 = new MyRegularClass()::m;
    Consumer<String> c4 = new MyGenericClass<String>()::m;

    for (Consumer<String> c : asList(c1, c2, c3, c4)) {
      c.accept("regular invocation of accept(String)");

      for (Method m : c.getClass().getDeclaredMethods()) {
        String n = m.getName() + "(" + m.getParameters()[0].getType().getSimpleName() + ")";
        try {
          m.invoke(c, n);
        } catch (Exception e) {
          System.out.println("Did not accept String: " + n + " => " + e);
        }
        try {
          m.setAccessible(true);
          m.invoke(c, new StringBuilder("StringBuilder of ").append(n));
        } catch (Exception e) {
          System.out.println("Did not accept StringBuilder: " + n + " => " + e);
        }
      }
      System.out.println("-----");
    }
  }
}

/* ==========================================
Output:

Lambda: regular invocation of accept(String)
Lambda: accept(Object)
Did not accept StringBuilder: accept(Object) => java.lang.reflect.InvocationTargetException
-----
Anonym: regular invocation of accept(String)
Anonym: accept(String)
Did not accept StringBuilder: accept(String) => java.lang.IllegalArgumentException: argument type mismatch
Anonym: accept(Object)
Did not accept StringBuilder: accept(Object) => java.lang.reflect.InvocationTargetException
-----
MyRegularClass: regular invocation of accept(String)
MyRegularClass: accept(Object)
Did not accept StringBuilder: accept(Object) => java.lang.reflect.InvocationTargetException
Did not accept String: get$Lambda(MyRegularClass) => java.lang.IllegalAccessException: Class com.example.foo.SomeClass can not access a member of class com.example.foo.SomeClass$$Lambda$2/1175962212 with modifiers "private static"
Did not accept StringBuilder: get$Lambda(MyRegularClass) => java.lang.IllegalArgumentException: argument type mismatch
-----
MyGenericClass: regular invocation of accept(String)
MyGenericClass: accept(Object)
Did not accept StringBuilder: accept(Object) => java.lang.reflect.InvocationTargetException
Did not accept String: get$Lambda(MyGenericClass) => java.lang.IllegalAccessException: Class com.example.foo.SomeClass can not access a member of class com.example.foo.SomeClass$$Lambda$3/617901222 with modifiers "private static"
Did not accept StringBuilder: get$Lambda(MyGenericClass) => java.lang.IllegalArgumentException: argument type mismatch
-----
*/
于 2014-07-21T14:41:28.547 回答