I'll try to illustrate my problem in the following simplified example:
public class DataHolder<T> {
private final T myValue;
public DataHolder(T value) {
myValue = value;
}
public T get() {
return myValue;
}
// Won't compile
public <R> DataHolder<R super T> firstNotNull(DataHolder<? extends R> other) {
return new DataHolder<R>(myValue != null ? myValue : other.myValue); }
public static <R> DataHolder<R> selectFirstNotNull(DataHolder<? extends R> first,
DataHolder<? extends R> second) {
return new DataHolder<R>(first.myValue != null ? first.myValue : second.myValue);
}
}
Here I want to write generic method firstNotNull
that returns DataHolder
parametrized by common supertype of type parameter T
of the this
and other
argument, so later I could write e.g.
DataHolder<Number> r = new DataHolder<>(3).firstNotNull(new DataHolder<>(2.0));
or
DataHolder<Object> r = new DataHolder<>("foo").firstNotNull(new DataHolder<>(42));
The problem is that this definition of firstNotNull
is rejected by compiler with message that super T
part of type constraint is illegal (syntactically).
However without this constraint definition is also wrong (obviously), because in this case T
and R
are unrelated to each other.
Interestingly, definition of similar static method selectFirstNotNull
is correct and the latter works as expected. Is it possible to achieve the same flexibility with non-static methods in Java type system at all?
Whenever you want to restrict the type parameter to subtypes of a particular class you can use the bounded type parameter. If you just specify a type (class) as bounded parameter, only sub types of that particular class are accepted by the current generic class. These are known as bounded-types in generics in Java.
Multiple BoundsBounded type parameters can be used with methods as well as classes and interfaces. Java Generics supports multiple bounds also, i.e., In this case, A can be an interface or class. If A is class, then B and C should be interfaces. We can't have more than one class in multiple bounds.
Object, you'll apply constraints to the type parameter. For example, the base class constraint tells the compiler that only objects of this type or derived from this type will be used as type arguments. Once the compiler has this guarantee, it can allow methods of that type to be called in the generic class.
Multiple parameters You can also use more than one type parameter in generics in Java, you just need to pass specify another type parameter in the angle brackets separated by comma.
It isn't possible to do this. The authors of Guava ran into the same issue with Optional.or
. From that method's documentation:
Note about generics: The signature
public T or(T defaultValue)
is overly restrictive. However, the ideal signature,public <S super T> S or(S)
, is not legal Java. As a result, some sensible operations involving subtypes are compile errors:Optional<Integer> optionalInt = getSomeOptionalInt(); Number value = optionalInt.or(0.5); // error FluentIterable<? extends Number> numbers = getSomeNumbers(); Optional<? extends Number> first = numbers.first(); Number value = first.or(0.5); // error
As a workaround, it is always safe to cast an
Optional<? extends T> to Optional<T>
. Casting either of the above exampleOptional
instances toOptional<Number>
(whereNumber
is the desired output type) solves the problem:Optional<Number> optionalInt = (Optional) getSomeOptionalInt(); Number value = optionalInt.or(0.5); // fine FluentIterable<? extends Number> numbers = getSomeNumbers(); Optional<Number> first = (Optional) numbers.first(); Number value = first.or(0.5); // fine
Since DataHolder
is immutable like Optional
, the above workaround will work for you too.
See also: Rotsor's answer to Bounding generics with 'super' keyword
I don't think there is any easy and type-safe way to do this. I've tried a couple of approaches, but the only working approach that I found is to start with a super
type generic instance, and make the method pretty simple like this:
public DataHolder<T> firstNotNull(DataHolder<? extends T> other) {
return new DataHolder<T>(myValue != null ? myValue : other.myValue);
}
Now you have to change your invocation to:
DataHolder<Number> r = new DataHolder<Number>(3).firstNotNull(new DataHolder<>(2.0));
You might argue that this doesn't really answer your question, but this is the simplest thing you're going to get, or better resort to a static
method approach. You can surely come up with some highly convoluted (and type-unsafe) methods to do so, but readability should be of major concern here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With