1

I implemented the following Java classes:

public class Data<T> { private List<T> data; public List<T> getData() { return this.data; } public Data<T> setData(List<T> data) { this.data = data; return this; } public Data<T> getAsNullIfEmpty() { if (this.data == null || this.data.isEmpty()) return null; return this; } } 
public class IntegerData extends Data<Integer> {} 

I would like the getAsNullIfEmpty() method to be able to be called by its subclasses instances.

The line IntegerData integerData = new IntegerData().getAsNullIfEmpty(); throws the following compilation error: incompatible types: Data<java.lang.Integer> cannot be converted to IntegerData

I tried changing the body of the method getAsNullIfEmpty() to this:

public <E extends Data<T>> E getAsNullIfEmpty() { if (this.data == null || this.data.isEmpty()) return null; return this; } 

This doesn't compile though because Data<T> does not extend itself. Is there a way to accomplish this without recurring to overriding the method in each of the child classes or using an explicit cast?

0

1 Answer 1

2

You need the "usual" trick for having a class's generics refer to itself, plus an unsafe cast.

public class Data<T, D extends Data<T, D>> { private List<T> data; public List<T> getData() { return this.data; } public D setData(List<T> data) { this.data = data; return (D) this; } public D getAsNullIfEmpty() { if (this.data == null || this.data.isEmpty()) return null; return (D) this; } } public class IntegerData extends Data<Integer, IntegerData> { ... } 
Sign up to request clarification or add additional context in comments.

5 Comments

Similar version of this idea can be found in Effective Java in Item 2 which discusses builder pattern. There instead of return (D) this; author uses return self(); which is calling protected abstract D self(); with is required to be overridden in subclass as protected [SubclassName] self(){ return this;} (we assume that D is defined to hold subclass name).
This solution solves the problem with casting. But means also, that you will not allow direct usage of Data class like 'd = new Data<Integer>()', because you will have to implement a subclass for each parameter type. It is not much work, but still some overhead.
@mentallurg I think you could actually get away with Data<Integer, ?> d = new Data<Integer, Data<Integer>>() in this version?
No :) Data<Integer> will not compile because here you have one type parameter instead of two.
Hmmm. And Data<Integer, ?> d = new Data<Integer, Data<Integer, ?>>()?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.