2

If you have only a Class object, how does one get a method reference to a method such as toString? Later we will have instances of this particular class on which we will invoke this method via the method reference.

For example, consider a Java enum, a subclass of Enum. Here T is defined as <T extends Enum>.

Class c = MyEnum.class
…
Function< T , String> f = c :: toString ;

I get an error saying "invalid method reference".

Holger
  • 285,553
  • 42
  • 434
  • 765
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154

3 Answers3

7

For toString, it's as easy as Object::toString. All Objects have toString, so you can use it right there. For other methods where you don't know statically that the object has that method, there's no easy way; you have to write a lambda that does it the ugly reflective way.

Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
4

If you can access your Class object using a class literal in the form Class<MyEnum> c=MyEnum.class;, it implies that the type MyEnum is known at compile time. In this case, the Class object is an unnecessary detour. You can access all methods of the class MyEnum using the form MyEnum::methodName, e.g.

Function<MyEnum,String> f=MyEnum::toString;

This is what the tutorial describes as “Reference to an Instance Method of an Arbitrary Object of a Particular Type”. It doesn’t require an actual MyEnum instance.

Nevertheless, there is no point in dealing with MyEnum when you want to have a Function<T,…> as that function must be able to consume arbitrary instances of T, not necessarily being MyEnum. So this function can only use methods existing in T and it doesn’t need to search them in MyEnum.

Since your target method is not specific to MyEnum, that’s possible:

Function<T,String> f=T::toString;

But as already pointed out, the method toString is defined in java.lang.Object, so you may also use the form

Function<T,String> f=Object::toString;

as a method declared for all objects may also get invoked on instances of T. Though even this is a bit pointless as you can also use

Function<Object,String> f=Object::toString;

reflecting the ability to consume any instance of Object, not just T. Carefully written Generic code will always use wildcards to avoid unnecessary restrictions regarding its input. So it will accept a function which can consume T (which implies the ability to consume MyEnum) without requiring its type parameter to exactly match that type. For example:

<R> Stream<R> map(Function<? super T,? extends R> mapper)
map, applied on a Stream<T> will accept a Function<Object,…> as Object is a supertype of T


So you can use T::methodName to access every method available for the bounds of that type, i.e. in your case you can use all methods of Enum and, of course, Object, but no methods specific to MyEnum not present in its supertypes. This isn’t different to ordinary method invocations you try to apply on instances of T. Further, a method not present in T wouldn’t be eligible for creating a valid Function<T,…> anyway.


If you want to create Function instances for methods not known at compile-time, you will have to use Reflection, and this is the only case where you have to deal with Class objects (in the context of method references). The second example of this answer shows how to create a Function for an instance method returning an object but that’s really only for those who know precisely what they are doing…

Note also that such reflectively created Functions have to use raw types as their appropriate generic type can’t be declared as it would refer to a type not present at compile-time.

Community
  • 1
  • 1
Holger
  • 285,553
  • 42
  • 434
  • 765
1

What you are trying here, is to get the reference of toString() for the class Class, which is probably not what you intended. As toString() is defined for all objects, this should work (not tested):

Function< T , String> f = t -> t.toString();
Sleafar
  • 1,486
  • 8
  • 10