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, is clear that Foo is not for sure an instance of T.
But this being an extended of Foo, still is Foo.
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>)
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);
}
Ideone demo
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.
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.
Related
I have a class hierarchy, and would like to build a list of attribute setters for each class in the hierarchy. The code I want to write is similar to the following:
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
class Attribute<E, T> {
public final Class<T> dataType;
public final BiConsumer<E, T> setter;
public Attribute(final Class<T> dataType, final BiConsumer<E, T> setter) {
this.dataType = dataType;
this.setter = setter;
}
}
class Foo1 {
public static final List<Attribute<Foo1, ?>> foo1Attrs = new ArrayList<>();
static {
foo1Attrs.add(new Attribute<>(String.class, Foo1::setProp1));
}
public void setProp1(final String prop1) {
}
}
class Foo2 extends Foo1 {
// The following line results in an error
public static final List<Attribute<Foo2, ?>> foo2Attrs = new ArrayList<>(foo1Attrs);
static {
foo2Attrs.add(new Attribute<>(Integer.class, Foo2::setProp2));
}
public void setProp2(final Integer prop2) {
}
}
When I compile the above, I get the error:
error: incompatible types: cannot infer type arguments for ArrayList<>
public static final List<Attribute<Foo2, ?>> foo2Attrs = new ArrayList<>(foo1Attrs);
^
reason: inference variable E has incompatible bounds
equality constraints: Attribute<Foo2,?>
lower bounds: Attribute<Foo1,?>
where E is a type-variable:
E extends Object declared in class ArrayList
I understand why I am getting the above error. My question is, what is the typically pattern employed to make the above code compile and run?
The only way I can figure out how to make this work is to create a copy constructor on the Attribute class that takes an Attribute<? extends E, T>, then duplicate the data members (pointers) stored in the Attribute class into a second memory location with a different type. This seems really heavy for what should be the equivalent of a static_cast in C++. The following changes exemplify this approach:
class Attribute<E, T> {
...
public final BiConsumer<? super E, T> setter;
...
public Attribute(final Attribute<? super E, T> other) {
this.dataType = other.dataType;
this.setter = other.setter;
}
}
class Foo2 extends Foo1 {
public static final List<Attribute<Foo2, ?>> foo2Attrs = new ArrayList<>();
static {
for (Attribute<Foo1, ?> attr : foo1Attrs)
foo2Attrs.add(new Attribute<>(attr));
...
}
...
}
To reiterate the question: Is there a better approach to address this compile-time error than that outlined above?
Since foo2Attrs can contain an Attribute<Foo1, ?> where Foo1 is a supertype of Foo2, it should be declared as type List<Attribute<? super Foo2, ?>>. This makes sense because a Foo2 object can have its attributes set by any attribute setter which targets a superclass of Foo2.
Likewise, foo1Attrs should be declared as type List<Attribute<? super Foo1, ?>>.
This gets rid of the compilation error caused by the first type parameter, but there'll still be a problem when you try to write some code like foo2Attrs.get(i).setter.accept(foo, 23), because the setter is inferred as type BiConsumer<? super Foo2, ?> and the integer 23 can't be converted to the second ?.
This question already has an answer here:
Why can this generic method with a bound return any type?
(1 answer)
Closed 4 years ago.
Why does javac not abort with a type error on this code example
import java.util.List;
public class StaticMethodWithBoundedReturnType {
static class Foo {
}
static class Bar extends Foo {
}
static <F extends Foo> F getFoo(String string) {
…
}
public static void main(String[] args) {
// Compiles without error, even though List does not extend Foo.
List<Integer> list = getFoo("baz");
}
}
Obviously List can never be a subtype of Foo. And even if there would exists a subtype of List which would somehow extend Foo, then the assigning of list at the call site of getFoo() should be invalid. I'm aware of the existence of type erasure. But shouldn't javac be able see that the type of list does not satisfy the bounded type constraint extends Foo and thus fail compilation with a type error?
Why is javac not able to typecheck the call site of a static method with a bounded type parameter as return type?
It appears I could get type safety with the following slight modification:
import java.util.List;
public class StaticMethodWithBoundedReturnType {
static class Foo {
}
static class Bar extends Foo {
}
static <F extends Foo> F getFoo(String string, Class<F> clazz) {
…
}
public static void main(String[] args) {
// Does not compile \o/
List<Integer> list = getFoo("baz", List.class);
}
}
But this requires adding the Class parameter to getFoo() which isn't used in the method's body at all. Is there a better way to achieve type safety?
To understand this, we need to understand what the following actually means:
static <F extends Foo> F getFoo(String string) {
return null;
}
That says that getFoo returns a value of some type that must be inferred from the context in which the call is made. Furthermore, it makes the constraint, that the inferred type must be a subtype of Foo.
Since null is assignable to all possible reference types, it is suitable as a return value. In fact, it is the only possible that may be returned.
To illustrate, try the following variation:
import java.util.List;
public class StaticMethodWithBoundedReturnType {
static class Foo {
}
static class Bar extends Foo {
}
static <F extends Foo> F getFoo(String string) {
return new Bar();
}
public static void main(String[] args) {
// Compiles without error, even though List does not extend Foo.
List<Integer> list = getFoo("baz");
}
}
This gives a compilation error
StaticMethodWithBoundedReturnType.java:11: error: incompatible types:
Bar cannot be converted to F
return new Bar();
^
You may ask: Why isn't Bar compatible with F?.
Answer: because the F stands for ( R & ? extends Foo ) where R is the type that the result of getFoo is assigned to. And, within getFoo re cannot know what R will be. (Indeed, it can be lots of different types!)
In short, the type signature
<F extends Foo> F getFoo(String string)
is problematic. However, consider this:
static <F extends Foo> F getFoo(Class<F> clazz) {
return class.newInstance();
}
That is legal, and will return a value that satisfies runtime type safety (locally). But you will then get the expected compilation error if you try to assign it to a List<Integer>:
StaticMethodWithBoundedReturnType.java:16: error: incompatible types:
inference variable F has incompatible bounds
List<Integer> list = getFoo(Bar.class);
^
equality constraints: Bar
upper bounds: List<Integer>,Foo
where F is a type-variable:
F extends Foo declared in method <F>getFoo(Class<F>)
1 error
Returning to the example, consider the call:
List<Integer> list = getFoo("baz");
That is legal because the inferred type for the result is the intersection type (List<Integer> & ? extends Foo). Indeed, that intersection type is implementable; e.g. as
class Baz extends Bar implements List<Integer> { /* list methods */ }
(The fact that there is no implementation in this Baz class in our program is immaterial. There could be one.)
So, we can compile the program. And when we execute it, list will be assigned null, which is not a runtime type violation.
I have the following function which returns an List of the same type as which it is passed.
<T> List<T> foo(T bar)
{
...
}
I want to have this same function, except in the form of a Java Functional Interface. I've tried the following:
final <T> Function<T, List<T>> foo;
But it does not like the <T>. If I omit <T> like:
final Function<T, List<T>> foo;
It claims it cannot find type T, exactly the same error as if I were to define the original function as:
List<T> foo(T bar) // cannot find type 'T'
{
...
}
I want to have function foo as a first class function.
You’re confusing a method with a class, which may be implemented as a lambda.
If T isn’t a type in the context of the code, you can’t type anything.
You can do this:
class MyClass<T> implements Function<T, List<T>> {
public List<T> accept(T t) {
// some impl
}
}
I have the following scenario in Java generics:
public abstract class A<T> {
protected final Class<T> typeOfX;
public A(final Class<T> typeOfX) {
this.typeOfX = typeOfX;
}
public abstract void load(final T x);
}
public class AnyA<S> extends A<S> {
private final Map<String, A<? extends S>> map;
public AnyA(final Class<S> superTypeOfX,
final Map<String, A<? extends S>> map) {
super(superTypeOfX);
this.map = map;
}
#Override
public void load(final S superx) {
for (final A<? extends S> a: map.values())
if (a.typeOfX.isAssignableFrom(superx.getClass())) //Here I want to say: "if superx can be casted to a.typeOfX".
a.load(a.typeOfX.cast(superx)); //Here I want to cast superx to a.typeOfX (so as to call the load method). Here's the compile error.
}
}
I'm getting the error:
incompatible types: S cannot be converted to CAP#1
where S is a type-variable:
S extends Object declared in class AnyA
where CAP#1 is a fresh type-variable:
CAP#1 extends S from capture of ? extends S
AnyA is a composite A, i.e. is an A which maintains several other A instances.
AnyA in its load(...) method shall decide which of the maintained A instances should be used to "pass the loading process to" of the argument.
In other words, AnyA is responsible for finding the correct A to load the argument.
But also AnyA is an A because it handles loading the argument.
My question is:
Why is this cast not possible, by the time I know that S is a sub-class of T and all A instances in AnyA can load a subclass of S?
How can I overcome this problem without changing the class diagram too much?
I have read about "helper methods" but cannot match the example shown there to my problem.
I'm using NetBeans IDE with Java SDK 8.
Note that regardless of what you do, the code is not "syntactically type safe" in any case. There is an unchecked cast, and the only safety belt that prevents this from going wrong is the isAssignableFrom check.
(That is often OK, I'm just mentioning it for completeness)
The reason for the error may be more obvious when you pull the lines apart (here, S stands for SuperType, according to the Type Parameter Naming Conventions - please follow them!)
A<? extends S> a = ...;
S s = a.typeOfX.cast(s);
a.load(s);
The A<? extends S> intuitively means that it is an A that can accept an unknown type in its load method. You know that it extends type S, but you do not know which type this is.
It may become blatantly obvious when you insert Object for S:
A<String> specificA = ...;
// So the "specificA" can load "String" objects. Then this is fine:
A<? extends Object> a = specificA;
Object s = a.typeOfX.cast(s);
// But here's the error: "s" is only an Object, and not a String!
a.load(s);
I think the main point of confusion (and the main reason for the question) was the following: When calling
Object s = a.typeOfX.cast(s);
and typeOfX is String.class, then the return type of the cast will not be String, but only the type that the compiler can infer at this point. And this is Object, in the example above.
However, you already referred to the Helper Methods, and indeed, with some trickery, you can make this compile,
but... (see notes below)
import java.util.Map;
abstract class A<T>
{
protected final Class<T> typeOfX;
public A(Class<T> typeOfX)
{
this.typeOfX = typeOfX;
}
public abstract void load(T x);
}
class AnyA<S> extends A<S>
{
private final Map<String, A<? extends S>> map;
public AnyA(Class<S> superTypeOfX,
Map<String, A<? extends S>> map)
{
super(superTypeOfX);
this.map = map;
}
#Override
public void load(S s)
{
for (A<? extends S> a : map.values())
{
if (a.typeOfX.isAssignableFrom(s.getClass()))
{
callLoad(a, s);
}
}
}
private static <S, T extends S> T cast(A<T> a, S s)
{
T t = a.typeOfX.cast(s);
return t;
}
private static <T, S extends T> void callLoad(A<S> a, T s)
{
a.load(cast(a, s));
}
}
I would not recommend this in practice.
Personally and subjectively: I think that when you are doing the isAssignableFrom check, then the (unchecked) cast should be as close as possible to this check. Otherwise, the code will be very hard to understand.
So although unchecked casts are a code smell in practice, and I try to avoid SuppressWarning whenever possible, I would consider this as far more readable:
for (A<? extends S> a : map.values())
{
if (a.typeOfX.isAssignableFrom(superx.getClass()))
{
// This call is safe as of the check done above:
#SuppressWarnings("unchecked")
A<Object> castA = (A<Object>) a;
castA.load(superx);
}
}
I'm trying to figure out why this code does not compile on JDK 1.8.0_45:
public class Example<E extends Example<E>> {
public List<? extends Example<?>> toExamples(Collection<String> collection) {
return collection.stream()
.map(v -> lookup(v))
.collect(Collectors.toList());
}
public static <E extends Example<E>> E lookup(String value) {
return null;
}
}
Adding a seemingly unnecessary cast fixes it:
public class Example<E extends Example<E>> {
public List<? extends Example<?>> toExamples(Collection<String> collection) {
return collection.stream()
.map(v -> (Example<?>) lookup(v))
.collect(Collectors.toList());
}
public static <E extends Example<E>> E lookup(String value) {
return null;
}
}
Here's the error from the compiler:
Example.java:9: error: incompatible types: inference variable R has incompatible bounds
.collect(Collectors.toList());
^
equality constraints: List<Object>
upper bounds: List<? extends Example<?>>,Object
where R,A,T are type-variables:
R extends Object declared in method <R,A>collect(Collector<? super T,A,R>)
A extends Object declared in method <R,A>collect(Collector<? super T,A,R>)
T extends Object declared in interface Stream
For some reason, the return type of lookup() isn't correctly inferred to something extending Example.
As Peter Lawrey pointed out, ? extends Example<?> is not compatible with E extends Example<E>. Still, even fixing the signature doesn’t make type inference work here.
The reason is a known limitation of the type inference as it does not back-propagate through chained method invocations. In other words, the return type allows to infer the types for the collect(…) invocation but not for the preceding map(…) invocation. (see also this answer)
But it works for nested method invocations, so the following rewritten method can be compiled:
public class Example<E extends Example<E>> {
public <E extends Example<E>> List<E> toExamples(Collection<String> collection) {
return collection.stream()
.collect(Collectors.mapping(v -> lookup(v), Collectors.toList()));
}
public static <E extends Example<E>> E lookup(String value) {
return null;
}
}
Still, you have to rethink the semantics of your code. A method’s type parameter which appears only at the return type can’t be correct as it implies that “whatever the caller substitutes for this type parameter, the method will return the right thing”. Since the method implementation doesn’t know what the caller assumes, this is impossible. Only returning null or an empty list will work correctly, which is of little use.
When you have a ? it doesn't equal another ? i.e. the compiler doesn't see
? extends Example<?>
as a match for
E extends Example<E>
as it cannot assume the two ? are the same. It could be
A extends Example<B>
When you perform the cast, you obscure the constraint so it can match.
My guess is that the generic type defined in the static method is not the same as the generic type defined in the class. You should be able to make the lookup method non-static so it matches the same type defined in the class level generic declaration:
public E lookup(String value) {
return null;
}