1

Suppose you have a Method

void m(String s) {} 

by using the new Method References you can create a Consumer from it

Consumer<String> c = Class::m;

If you now take a look at the class of the created Consumer via reflection you will see that its only declared method is

void accept(Object)

If had created the Consumer the old way by creating an anonymous inner class

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

there would be the bridge method void accept(Object) as well as void accept(String).

now lets say we have to pass this consumer around and thereby loose its generic type. If you had an implementation of Consumer<String> rather than a Lambda expression you could get it back by looking using relfection to access its methods. Since the consumer created via method reference does only have the general method this wont work. Is there a way to get the parameter type of the consumer created by method reference?

Blank Chisui
  • 1,043
  • 10
  • 25
  • I don't understand the question. Can you perhaps give some more information about where exactly are those methods you've shown? Also, clarify your question a bit. It's hard to understand what is being asked. – Rohit Jain Mar 07 '14 at 16:11
  • @Blank Chisui, you should clarify that you're "looking" at the lambda and anon inner class versions of the Consumer using reflection. – Stuart Marks Mar 07 '14 at 16:40
  • The short answer is probably not, since this information is erased. For the AIC case, bridge methods are created by javac in order to get generic subtyping to work. For lambda, the initial invocation returns a MethodHandle pointing to accept(Object) so there is no need for a bridge method. – Stuart Marks Mar 07 '14 at 16:45
  • Even if you lose its generic type, if you try to invoke the `accept()` method with anything other than a `String`, you will get a `ClassCastException`. – Sotirios Delimanolis Mar 07 '14 at 16:46
  • @StuartMarks: Yeah, but I hoped there would be a way. – Blank Chisui Mar 07 '14 at 16:57
  • @SotiriosDelimanolis: that would be the last resort. – Blank Chisui Mar 07 '14 at 16:57
  • 1
    Seems to be the same question as http://stackoverflow.com/questions/21887358/reflection-type-inference-on-java-8-lambdas – Holger Mar 11 '14 at 16:54

1 Answers1

1

Invocation of a raw Consumer (without a generic type) always results in a warning. You simply should not do that as you do not know the type. Raw types do not have a type. That didn't change with Java 8.

The interface Consumer has a generic type but you do not know that at runtime. Even not for lambdas. Some classes might actually have the information, but that wont help you. If you actually need to know the type then simply create such an interface.

  @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());
  }

Here's some example code and output to see what methods there are and how they can and can not ne invoked:

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
-----
*/
Claude Martin
  • 745
  • 6
  • 21