2

Is there any way I can reference this::method using a more generic type like below?

Number method(Integer input){ return 1;}

void test(){
    Function<Integer, Number> ref = this::method; //OK
    Function<Number, Number> moreGenericRef = this::method // does not compile.
    Function<? extends Number, Number> moreGenericRef2 = this::method // does not compile.
}

I want to be able to do the following.

Map<String, Function<Number,Number>> maps;
maps.add("method1", this::method)
maps.add("method2", this::method2)

maps.get("methods1").apply(1.2);
maps.get("methods2").apply(1);

The functions are adapters that will be called by the mapper of Stream< Number >

dwong
  • 103
  • 5
  • 14
  • 1
    Possible duplicate of [What is PECS (Producer Extends Consumer Super)?](https://stackoverflow.com/questions/2723397/what-is-pecs-producer-extends-consumer-super) – Izruo Feb 05 '18 at 19:39
  • 3
    No. You can't pass any `Number` to a method expecting `Integer`. – shmosel Feb 05 '18 at 19:40
  • if you change your method like this `Number method(Number input){ return 1;}` , then all three compiles – pvpkiran Feb 05 '18 at 19:40
  • @pvpkiran but then method won't' have access to the methods inside Integer.class, unless I explicitly downcast. – dwong Feb 05 '18 at 19:47
  • 2
    The real mistake is that you think you need this. You might want to describe your actual requirement, instead of presenting us with an [XY Problem](http://xyproblem.info). – shmosel Feb 05 '18 at 19:48
  • @shmosel I want to store the functions as values in a map in an abstract class, and later accessed by its subclasses via, say, a string key. – dwong Feb 05 '18 at 19:59
  • 1
    @dwong: that's correct, and that's how it _should_ be. If you "need access to the methods inside Integer.class" then you don't actually _have_ a `Function`, and no amount of shenanigans will change that. The Java compiler is doing the right thing here. – Louis Wasserman Feb 05 '18 at 20:10
  • @shmosel all the functions/methods have similar behavior and the input extends the same superclass. I want to write the code more abstractly so that it will execute a function in the map by passing the input's superclass type. – dwong Feb 05 '18 at 20:11
  • @LouisWasserman Thanks for your answer. The access to methods in Integer.class will happen only inside "method." I have updated my description with my requirements. I probably can work around by changing my design, but i am wondering if I could achieve what i wanted. – dwong Feb 05 '18 at 20:23
  • 1
    It doesn't matter that it only occurs inside method. This has nothing to do with lambdas, really. You can't implement a method that takes an arbitrary Number by passing it to method. – Louis Wasserman Feb 05 '18 at 21:33

2 Answers2

2

You'd need the argument of the function to be contravariant instead of covariant:

Function<? super Integer, ? extends Number> moreGenericRef2 = this::method;

This compiles fine, and also allows for the return type to be any descendant of Number.

See PECS, which stands for Producer Extends, Consumer Super. See also covariance and contravariance for an in-depth introduction of this subject.

fps
  • 33,623
  • 8
  • 55
  • 110
  • thanks for your answer; however, it doesn't compile `incompatible types: invalid method reference Function super Number, ? extends Number> moreGenericRef2 = this::method; ^ incompatible types: Number cannot be converted to Integer` – dwong Feb 05 '18 at 19:45
  • True, but it's not clear that OP has such a method or was trying to assign it. – shmosel Feb 05 '18 at 20:00
2

Well if you change the definition to:

Function<? extends Integer, ? extends Number> moreGenericRef2 = this::method;

or

Function<Integer, ? extends Number> moreGenericRef2 = this::method;

or

Function<Integer, Number> moreGenericRef2 = this::method;

But having a definition such as ? extends Integer (where Integer is a final class makes no sense anyway)

Eugene
  • 117,005
  • 15
  • 201
  • 306