I'm trying to create a generic method that accepts two typed arguments, one of them bounded by itself,
class Foo
{
<T extends Foo, V> void myself(final Optional<V> value, final BiConsumer<T, V> destination)
{
if (value.isPresent())
{
destination.accept(~~this~~, value.get());
}
}
}
but compiler blames on the this
argument, because
error: incompatible types: Foo cannot be converted to T
destination.accept(this, value.get());
^
where T,V are type-variables:
T extends Foo declared in method <T,V>myself(Optional<V>,BiConsumer<T,V>)
V extends Object declared in method <T,V>myself(Optional<V>,BiConsumer<T,V>)
If T
is a subtype of Foo
, I don't understand why this
(which is a Foo
for sure) cannot be a T
.
Forcing the (T) this
cast seems to ""work"".
Update
I want to use it the following way,
class Bar extends Foo
{
void setAnswer(Integer toLife)
{
}
}
----
void outThere(Bar bar)
{
bar.myself(Optional.of(42), Bar::setAnswer);
}
The proposal of wildcarded argument
class Foo
{
<V> void myself(final Optional<V> value, final BiConsumer<? super Foo, V> destination)
{
if (value.isPresent())
{
destination.accept(this, value.get());
}
}
}
fails on the usage with,
error: incompatible types: invalid method reference
bar.myself(Optional.of(42), Bar::setAnswer);
^
method setAnswer in class Bar cannot be applied to given types
required: Integer
found: Foo,V
reason: actual and formal argument lists differ in length
where V is a type-variable:
V extends Object declared in method <V>myself(Optional<V>,BiConsumer<? super Foo,V>)
CodePudding user response:
T extends Foo
It's bounded by Foo
, but it isn't necessarily actually Foo
. It could be any subtype of Foo
instead.
Instead of defining a type variable, use a wildcard:
final BiConsumer<? super Foo, V> destination
Also, a better way to write the method body is:
value.ifPresent(consumer);
(There isn't really much advantage in invoking your method over just doing this directly).
Update for your update:
If you want to express something resembling a self type, you need to add another type variable to the class:
class Foo<F extends Foo<F>>
{
<V> void myself(final Optional<V> value, final BiConsumer<? super F, V> destination) {
if (value.isPresent())
{
// (F) is an unchecked cast, but is necessary, because
// nothing constrains F to actually be "itself".
destination.accept((F) this, value.get());
}
}
Then the Bar
class is defined as:
class Bar extends Foo<Bar> {
void setAnswer(Integer toLife) { /* ... */ }
}
Then the outThere
method works fine:
void outThere(Bar bar)
{
bar.myself(Optional.of(42), Bar::setAnswer);
}
CodePudding user response:
Let's say, Foo
has two subclasses, Foo1
and Foo2
, both not overriding the myself()
method. Then:
Foo1 me = ...;
Optional<String> value = ...
BiConsumer<Foo2,String> consumer = ...;
me.myself(value, consumer);
matches
<T extends Foo, V> void myself(final Optional<V> value, final BiConsumer<T, V> destination) {...}
with V
being String
and T
being Foo2
, while this
is of class Foo1
, so you can't pass it into a Foo2
consumer.
And that's what the compiler detected.
CodePudding user response:
The problem is that there is no guarantee that your T
is compatible with this
.
It could that the BiConsumer
is referring to a something that extends T
, then T
would not fit in. The issue is that you are inferring T
and that might not be compatible with this
.
If you really want this, then you should remove T
all together and just use Foo
.
If you want anything that extends Foo
and wishes to infer that, then you could use super
instead.
<V> void myself(final Optional<V> value, final BiConsumer<? super Foo, V> destination) {
if ( value.isPresent() ) {
destination.accept(this, value.get());
}
}
You are going to find some issues with this approach though.
Otherwise, you could also use Foo directly as mentioned:
public static class Foo {
<T, V> void myself(final Optional<V> value, final BiConsumer<Foo, V> destination) {
if ( value.isPresent() ) {
destination.accept(this, value.get());
}
}
}
Otherwise, if you are really sure you could cast it, but that is not really recommended.