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.
