I would like to pass generic method reference as a parameter in java method.
Just for an example I have prepared some abstract
public abstract class AbstractFoobar{
abstract String getLiteral();
}
and 2 classes that its extend. Classes have String parameter constructor.
public class Bar extends AbstractFoobar{
String literal;
public Bar(String literal) { this.literal = literal; }
public String getLiteral() { return literal; }
}
public class Foo extends AbstractFoobar{
String literal;
public Foo(String literal) { this.literal = literal; }
public String getLiteral() { return literal; }
}
I have a simple generic method that creates new instance of the Objects. The creation bases on Function creator parameter
public <T extends AbstractFoobar> T foo(T foobar, Function<String, T> creator) {
return creator.apply("foo" + foobar.getLiteral());
}
This example works perfectly when the method foo is executed with specific method reference Bar::new.
@Test
void test()
Bar bar = new Bar("bar");
Bar foobar = foo(bar, Bar::new);
assertEquals(Bar.class, foobar.getClass());
assertEquals("foobar", foobar.getLiteral());
}
But I dont know how to pass the reference method through the wrapper method fooGenerator
public <T extends AbstractFoobar> T fooGenerator(T foobar) {
//#1 return foo(foobar, T::new);
//#2 return foo(foobar, foobar.getClass().getConstructor(String.class));
}
#1 Compiler cannot instantiate the type T
#2 the method foo(..., Function<>) is not applicable for the argument Constructor<>
A very important, but never emphasized enough, point about Java generics is that they are just a fancy way to avoid writing explicit casts . They are nothing more, nothing less.
So, if you can't write:
public AbstractFoobar fooGenerator(AbstractFoobar foobar) {
return foo(foobar, /* something here, involving AbstractFoobar and casts */::new);
}
// Call site
Bar foobar = (Bar) foo(bar);
then you can't do it with generics. And there isn't such a thing, because constructors do not participate in inheritance: AbstractFoobar::new
would create an instance of AbstractFoobar
(if it could be instantiated), not a subclass.
For you current AbstractFoobar
definition, you can't do anything better than just invoking foo
directly, with the explicit arguments.
The only way you could do it with a single parameter is if the AbstractFoobar
had a factory method on it, eg
public abstract class AbstractFoobar<A extends AbstractFoobar<A>> {
// ...
abstract A newInstance(String arg);
}
public class Bar extends AbstractFoobar<Bar> {
// ...
Bar newInstance(String arg) { return new Bar(arg); }
}
which you could then use like:
public <T extends AbstractFoobar<T>> T fooGenerator(T foobar) {
return foo(foobar, foobar::newInstance);
}
Bar foobar = foo(bar, Bar::new);
is valid because you reference the constructor for a specific class.
Whereas:
foo(foobar, T::new)
tries to reference the constructor of a generic type. And that is simply not possible. Remember about type erasure, in the (very) end, there are just Object
s here, and casts. T::new
simply does not denote something meaningful.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.