I'm struggling with this aspect of Generics in Java. Hopefully someone can help me see the ways.
I have a class that holds a List of objects. This code works, but I want to get rid of the cast. How can I make this more generic?
public class Executor {
List<BaseRequest<BaseObj>> mRequests = new ArrayList<BaseRequest<BaseObj>>();
public Executor() {
}
#SuppressWarnings("unchecked")
public <T extends BaseObj> void add(final BaseRequest<T> request) {
mRequests.add((BaseRequest<BaseObj>) request);
}
public void execute() {
for (BaseRequest<BaseObj> r : mRequests) {
// DO SOMETHING WITH r
}
}
}
In the posted snippet you need the cast because BaseRequest<? extends BaseObj> is not a subtype of BaseRequest<BaseObj>, and the cast can't be checked at runtime because of type erasure, and that's why the compiler warns you. But if you change the declaration of mRequests:
public class Executor {
List<BaseRequest<? extends BaseObj>> mRequests = new ArrayList<>();
public Executor() {
}
public <T extends BaseObj> void add(final BaseRequest<T> request) {
mRequests.add(request);
}
public void execute() {
for (BaseRequest<? extends BaseObj> r : mRequests) {
// DO SOMETHING WITH r
}
}
}
class BaseRequest<T> {}
class BaseObj {}
Let's resolve the problem step-by-step. You want to be able to call
req.add(new BaseRequest<ExtObj1>());
req.add(new BaseRequest<ExtObj2>());
req.add(new BaseRequest<ExtObj3>());
where ExtObj[1|2|3] extends BaseObj. Given the List interface:
List<T> {
void add(T el);
}
we need to find a common supertype for BaseRequest<ExtObj1>, BaseRequest<ExtObj2> and BaseRequest<ExtObj3>. One supertype is BaseRequest<?> and another one is BaseRequest<? extends BaseObj>. I picked the second one because it's the most restrictive possible. You should know that in Java BaseRequest<ExtObj1> is not a subtype of BaseRequest<BaseObj> because generics are invariant.
Now that we have the right declaration for mRequests, finding the API for Executor.add() is straightforward. BTW, if the method body you need is really that simple, you don't even need the type parameter:
public void add(BaseRequest<? extends BaseObj> request) {
mRequests.add(request);
}
Warnings are not errors. Warnings are there so you check if you have an error because it may not be checked automatically. You should check it and then use the annotation to note that warning was already checked.
In your case it warns BaseRequest<T> is not equivalent to BaseRequest<BaseObj>.
Example:
public class NumberWrapper<N extends Number> {
private N value;
public void setValue(N value) {
this.value = value;
}
}
public class MainClazz {
private NumberWrapper<Integer> wrappedNumber = new NumberWrapper<Integer>();
public void run() {
Number n = externalSource.getNumber();
wrappedNumber.setValue(n); // <-- Error. What if getNumber returns a double?
}
}
You can have this error ir not depending on how you complete/integrate the code you are showing.
Related
Please consider the following code:
public abstract class Subject {
private Collection<Observer> observerCollection = new HashSet<>();
// ...
protected void notifyObservers() {
this.observerCollection.stream().filter(Objects::nonNull).forEach(o -> o.update(this));
}
}
public interface Observer<T extends Subject> {
void update(T subject);
}
I am getting the following compile-time warnings:
Observer is a raw type. References to generic type Observer should be parameterized
Type safety: The method update(Subject) belongs to the raw type Observer. References to generic type Observer should be parameterized
One comes at the call to update and for the life of me I can't figure out how to resolve it without using the warning suppressions. I've tried several ways to resolve the warning without luck. Any ideas on how this can be resolved?
Motivation
Consider the following client code:
public class IntegerContainer extends Subject {
private int integer;
public IntegerContainer(int integer) {
this.integer = integer;
}
public int getInteger() {
return this.integer;
} // ...
}
public class IntegerObserver implements Observer<IntegerContainer> {
private int cachedInteger;
#Override
public void update(IntegerContainer subject) {
this.cachedInteger = subject.getInteger(); // avoid cast here.
} // ...
}
The motivation for using generics in the Observer is to avoid a cast of the subject parameter so that the observer can retrieve the state of the subject.
This doesn't have anything to do with streams; it just straight up won't work.
An Observer<? extends Subject> is more or less unusable, because you don't know what subtype of Subject it's an observer of. For all you know, observerCollection only contains an Observer<SomeSubtypeOfSubjectThatNobodyEvenHeardOf>. (See the PECS principle; Observer is a consumer.)
I don't think there's any type-safe way to do this cleanly, frankly, because you can't say in Subject that the attached observers all accept this subtype of Subject, because there's no way to refer to "this subtype of Subject." The closest hack I can think of is
abstract class Subject<T extends Subject<T>> {
private Collection<Observer<? super T>> observers;
protected void notifyObservers() {
this.observerCollection.stream().filter(Objects::nonNull).forEach(o -> o.update((T) this)); // yes, this cast is unchecked
}
}
class SubSubject extends Subject<SubSubject> {
...
}
I'd focus on the value being passed between the Subject and Observer. I.e. both classes have one type parameter and the related methods make sure that the types are compatible:
public interface Observer<T> {
void update(T value); // no subject, but a value
}
public class Subject<T> {
private Collection<Observer<? super T>> observers = new HashSet<>();
protected void notifyObservers() {
this.observers.stream().filter(Objects::nonNull).forEach(o -> o.update(this.getValue()));
}
public void addObserver(Observer<T> observer) { // adding the right kind of observer
observers.add(observer);
}
abstract public T getValue(); // returning the value - this one is abstract
}
The key above is the abstract public T getValue(); method. Here is how you can write an IntegerContainer and and IntegerObserver :
public class IntegerContainer extends Subject<Integer> {
private int value;
public IntegerContainer(int value) {
this.value = value;
}
#Override
public Integer getValue() {
return value; // this is the parameter of the update() call
// you could even compute here something
// you can pass entire objects too, if required
}
}
public class IntegerObserver implements Observer<Integer> {
private int cachedInteger;
#Override
public void update(Integer value) {
this.cachedInteger = value; // no cast here
} // ...
}
You can put them together like this:
IntegerContainer container = new IntegerContainer(3);
IntegerObserver observer = new IntegerObserver();
container.addObserver(observer);
container.notifyObservers();
What I want is following:
Most of the time, the generic class will be like TestBuilder<X, X>, meaning that T and O are of the same type. Therefore I create two different constructor. I want to make anonoumous new calls like new TestBuilder<>(...) (I'm calling the <> anonoumous here).
Following 4 constructor example exists:
1) Working constructor calls
// Anonoumous, working
new TestBuilder<>(String.class, Integer.class)
.withOnNext(new Action1<Integer>()
{
#Override
public void call(Integer integer)
{
}
});
// not anonoumous, classified, working
new TestBuilder<String, String>(String.class)
.withOnNext(new Action1<String>()
{
#Override
public void call(String string)
{
}
});
2) Constructor Calls with problems or not working
// Anonoumous and working
// PROBLEM: withOnNext is called with Object instead of String
new TestBuilder<>(String.class)
.withOnNext(new Action1<Object>()
{
#Override
public void call(Object o)
{
}
});
// Anonoumous and NOT working
// this is what I want to work!
new TestBuilder<>(String.class)
.withOnNext(new Action1<String>()
{
#Override
public void call(String string)
{
}
});
Question
Is there a way to get the 4th constructor to work? I don't want to be forced to give the constuctor two classes if I call it with one argument only, the second generic class should "inherit" from the first in this case... Instead of having to write new TestBuilder<String, String>(String.class) I want to write new TestBuilder<>(String.class) or at least new TestBuilder<String>(String.class)...
Class
This is what the test builder class looks like:
public class TestBuilder<T, O>
{
public TestBuilder(Class<T> eventClass)
{
this(eventClass, (Class<O>)eventClass);
}
private TestBuilder(Class<T> eventClass, Class<O> observableClass)
{
init();
}
public TestBuilder<T, O> withOnNext(Action1<O> actionNext)
{
mActionNext = actionNext;
return this;
}
}
I don't think Java can infer the second generic type without some kind of hint. One way is giving the type in variable declaration:
TestBuilder<String, String> testBuilder = new TestBuilder<>(String.class);
testBuilder.withOnNext(new Action1<String>() {
#Override
public void call(String string) {
//...
}
});
But you'd still need to declare both generic parameters.
What I would do is encapsulating the information that both T and O are the same in a static factory method:
public class TestBuilder<T, O> {
public static <T> TestBuilder<T, T> create(Class<T> eventClass) {
return new TestBuilder<T, T>(eventClass);
}
// ...
}
and then call it like this:
TestBuilder.create(String.class).withOnNext(...);
Yet another option is encapsulating the information in a separate class inheriting from TestBuilder:
public class SimpleTestBuilder<T> extends TestBuilder<T,T> {
public SimpleTestBuilder(Class<T> eventClass) {
super(eventClass, eventClass);
}
}
public class TestBuilder<T, O> {
private TestBuilder(Class<T> eventClass, Class<O> observableClass) {
}
// ...
}
Used as
new SimpleTestBuilder<>(String.class).withOnNext(...);
Yet another good option is to encapsulate the information O is same as T in a static method:
public class TestBuilder<T, O> {
public static <T> TestBuilder<T, T> create(Class<T> eventClass) {
return new TestBuilder<T, T>(eventClass);
}
// ...
}
Used as
TestBuilder.create(String.class).withOnNext(...);
You can introduce a helper type variable for the first constructor like this:
public class TestBuilder <T, O>
{
public <H extends T, O> TestBuilder(Class<H> c)
{
this((Class) c, (Class) c);
}
public TestBuilder(Class<T> c1, Class<O> c2)
{
// ...
}
public static void main(String[] args)
{
TestBuilder<String, String> hw = new TestBuilder<>(String.class);
System.out.println(hw);
}
}
This will create some unchecked warnings for the constructor, but not at the call site. Please note though that some might consider this ad practise, especially since not everyone knows about constructor type parameters. For the sake of completeness, the explicit invocation of the constructor has to look like this:
new<String> TestBuilder<>(String.class).doStuff()
public class BinaryVertex {
public BinaryVertex parent,left,right;
}
public class BSTVertex extends BinaryVertex {
public void foo() {
left = new BSTVertex();
if(Math.floor(Math.random()*2) == 0) left.foo();
}
}
I'm making a tree / graph api for school, approaching it from a oop standpoint. But im trying to figure out a way for the inherited class to treat some of its base class variables as its own type (i.e. parent,left,right should be treated as BSTVertex when called from BSTVertex but treated as BinaryVertex when called from BinaryVertex) without casting.
I'm thinking of generics but I'm not sure how to implement that in this situation.
UPDATE
Nice, didnt know you could use extend in generics. But I'm getting a BSTVertex<T> cannot be converted to T error with this:
public class Test {
public static void main(String[] args) {
new AVLVertex();
BSTVertex<BSTVertex> v = new BSTVertex<BSTVertex>();
v.foo();
}
class BinaryVertex<T extends BinaryVertex> {
public T parent, left, right;
}
class BSTVertex<T extends BSTVertex> extends BinaryVertex<T> {
public T foo() {
return this; //error here
}
}
class AVLVertex extends BSTVertex<AVLVertex> {
// this might probably end up being abstract too
}
foo needs to return a vertex of the same type as caller, i.e. if AVLVertex calls foo its expecting to get AVLVertex not BSTVertex
Yes, you can use generics like this:
public class BinaryVertex<T extends BinaryVertex<T>> {
public T parent, left, right;
}
public class BSTVertex extends BinaryVertex<BSTVertex> {
public void foo() {
left = new BSTVertex();
if(Math.floor(Math.random()*2) == 0) left.foo();
}
}
The same way the Comparable interface implemented, so subclasses receive the same type to compareTo method. For example, Integer implements Comparable<Integer>, so its compareTo method receives Integer argument.
Also please note the it would be better to create your own random number generator like this:
public class BSTVertex extends BinaryVertex<BSTVertex> {
private static final Random r = new Random();
public void foo() {
left = new BSTVertex();
if(r.nextBoolean()) left.foo();
}
}
UPDATE
In your updated code (in future please ask new question instead) you cannot safely cast, because you can potentially write later:
class RBVertex extends BSTVertex<RBVertex>{}
class AVLVertex extends BSTVertex<RBVertex>{}
This is ok from the compiler's point of view, but your AVLVertex generic argument is actually not an AVLVertex. That's why you have a compilation error in foo() method: your class can be later possibly extended in the way that would make your T incompatible with this.
You can fix this problem by doing an unchecked cast:
#SuppressWarnings("unchecked")
public T foo() {
return (T) this;
}
In this way if you mistakenly create class AVLVertex extends BSTVertex<RBVertex>{}, it will still compile, but upon calling AVLVertex.foo() you may have a runtime ClassCastException.
Suppose I have this codebase:
public class DataIn {}
public interface DataOut {}
public class DataSpecial1 implements DataOut {}
public class DataSpecial2 implements DataOut {}
public class TranslatorAndHandler<T extends DataOut>{
public T translate(DataIn dataIn);
public void handle(T t);
}
public class TranslatorImpl1 implements TranslatorAndHandler<DataSpecial1> {
public DataSpecial1 translate(DataIn dataIn){
// Some code
return null;
}
public void handle(DataSpecial1 data){}
}
public class TranslatorImpl2 implements TranslatorAndHandler<DataSpecial2> {
public DataSpecial2 translate(DataIn dataIn){
// Some code
return null;
}
public void handle(DataSpecial2 data){}
}
public class Wrapper {
public static TranslatorAndHandler<? extends DataOut> getCorrectTAH(){
if(someValue) {
return new TranslatorImpl1();
}
return new TranslatorImpl2();
};
private static final TranslatorAndHandler<? extends DataOut> tah = getCorrectTAH();
private DataOut savedData;
public Wrapper(DataIn dataIn) {
savedData = tah.translate(dataIn);
}
public void handleData() {
tah.handle(savedData); // Compiler won't accept this line
}
}
A bit of context: I write a model format (DataIn) that is read by a reader and then passed on the the Model (Wrapper). As different users will have different OpenGL-capabilites I externalized the rendering (TranslatorAndHandler). This would allow me to load the correct OpenGLHandler at runtime (getCorrectTAH()) and then make this Handler translate the read raw data into an optimized format for his own purposes.
Any human can tell that there is no problem with the code and that it is indeed typesafe. How can I remodel (redesign?) my approach so that this will work?
Currently it's showing this compiler error:
The method handle(capture#1-of ? extends DataOut) in the type
TranslatorAndHandler is not applicable
for the arguments (DataOut)
You pass an instance of type DataOut to tah.handle, however, it expects some subtype of it - for example, it should be clear that the following code should not compile:
interface DataOut { }
interface SomeDataOut extends DataOut { }
class TranslatorAndHandler<T extends DataOut> {
public void handle(T t) { }
}
public class Main {
public static void main(String[] args) {
TranslatorAndHandler<SomeDataOut> tah = null;
DataOut t = null;
tah.handle(t);
}
}
And it indeed causes a compilation error:
incompatible types: DataOut cannot be converted to SomeDataOut
A possible fix is making Wrapper class generic like this:
public class Wrapper<T extends DataOut> {
private final TranslatorAndHandler<T> tah = getCorrectTAH();
private T savedData;
public Wrapper(DataIn dataIn) {
savedData = tah.translate(dataIn);
}
public void handleData() {
tah.handle(savedData);
}
}
However, in this case tah field can't be static, since it's not possible to refer to type parameters from static members.
Generics are a compile time concept in Java. With a declaration like
public class TranslatorAndHandler<T extends DataOut>{
any variable declaration (or expression) of type TranslatorAndHandler must (really should, don't use raw types) provide a type argument to bind to the type parameter T. If you provide one, you know what type it is and you can use it. If you provide a wildcard, you don't know what type it is and therefore can't use it.
Your Wrapper class must know what type it is handling. If, at compile time, you don't know what getCorrectTAH() returns, ie. it's declared like this
public TranslatorAndHandler<?> getCorrectTAH() {...}
then you cannot expect to be able to use TranslatorAndHandler type parameter, since it is bound to ? which you cannot know at compile time.
It doesn't seem like TranslatorAndHandler should be generic in the first place. There's nothing special it can do with subtypes of DataOut.
By changing the wrapper class to the following one can dodge the compiler error:
public class Wrapper {
// Will be casted
#SuppressWarnings("unchecked")
private static final TranslatorAndHandler<DataOut> tah =
(TranslatorAndHandler<DataOut>) getCorrectTAH();
private DataOut savedData;
public Wrapper(DataIn dataIn) {
savedData = tah.translate(dataIn);
}
public void handleData() {
tah.handle(savedData); // Accepted because we can be sure that this cast will not fail
}
}
As said one can be sure that this is type-safe so this way one doesn't get a compiler error.
I wonder if there is a way to eliminate the rawtypes warning when I use generic classes that refer to each other:
public class DummyDeleteMe {
abstract class RightSide<L extends LeftSide>{ //Can I use a type parameter here?
L getLeftSide() {return _mate;}
void setLeftSide(L mate) {_mate = mate;}
L _mate;
void connectToMate(){
getLeftSide().setRightSide(this);//warning:
/** [unchecked] unchecked call to setRightSide(R) as a member of the raw type DummyDeleteMe.LeftSide
where R is a type-variable: * **/
}
}
abstract class LeftSide<R extends RightSide>{// And here?
R getRightSide(){return _mate;}
void setRightSide(R mate) {_mate = mate;}
R _mate;
}
class RightSideSub extends RightSide<LeftSideSub>{
void connectToMate(){
getLeftSide().setRightSide(this);//No warning
}
}
class LeftSideSub extends LeftSide<RightSideSub>{}
}
The compiler warning is because the LeftSide in the parameter bound is a raw type. Replacing it with LeftSide<?> causes an error in connectToMate. Overriding connectToMate in RightSideSub, the same code does not generate a warning.
I guess I'm looking for some kind of second type parameter, that is self referential:
abstract class RightSide<R extends RightSide<R,L>, L extends LeftSide<L,R>>{}
But that causes other type mis-match errors in the R variables and methods that return R.
I know this may not be exactly what you are looking for, but it may solve your problem. Based only on what you've shown in your code sample, there is no need for a generic at all. It's over complicating the issue. That is, if your example is a fairly close match for what you need.
Assuming it is, how about something like this.
public interface Half{
public Half getOtherHalf();
}
public class RightSide implements Half{
private LeftSide leftSide;
public void setLeftSide(LeftSide leftSide){
this.leftSide = leftSide;
}
#Override
public LeftSide getOtherHalf() {
return leftSide;
}
}
public class LeftSide implements Half{
private RightSide rightSide;
public void setRightSide(RightSide rightSide){
this.rightSide = rightSide;
}
#Override
public RightSide getOtherHalf(){
return rightSide;
}
}
Here is one solution. I have removed references to L for "left side" and R for "right side" and replaced them with T for "this side" and O for "other side"
public class Solution1 {
abstract static class Side<T extends Side<T, O>, O extends Side<O, T>> {
Side<O, T> _mate = null;
Side<O, T> getOtherSide() {
return _mate;
}
void setOtherSide(Side<O, T> mate) {
_mate = mate;
}
void connectToMate() {
getOtherSide().setOtherSide(this);
}
}
//You concrete implementations can replace This and Other
//with Right and Left as you see fit.
static class RightSideSub extends Side<RightSideSub, LeftSideSub> {
}
static class LeftSideSub extends Side<LeftSideSub, RightSideSub> {
}
}
However, I would suggest another, simpler solution at the cost of loosing connectToMate() method and replacing it with code in main() method below:
class OtherSolution{
abstract static class Side<O>{
O _mate = null;
void setOtherSide(O mate){
_mate = mate;
}
}
static class RightSideSub extends Side<LeftSideSub> {
}
static class LeftSideSub extends Side<RightSideSub> {
}
public static void main(String[] args) {
RightSideSub rs = new RightSideSub();
LeftSideSub ls = new LeftSideSub();
//do the connectToMate() operation externally like this:
rs.setOtherSide(ls);
ls.setOtherSide(rs);
}
}