Before I dive into generics....
Why don't you just introduce a CheckableView
and return that type?
public class CheckableView extends View implements Checkable {
}
private CheckableView getCheckableView() {
return checkableView;
}
If View
would be an interface it works:
interface View {}
interface Checkable {}
class CheckableView implements Checkable, View {}
public class Main {
private static CheckableView checkableView;
private static <T extends View & Checkable> T get() {
return (T) checkableView;
}
public static void main(String[] args) {
View view = Main.get();
Checkable checkable = Main.get();
}
}
I think the problem in understanding generics is that T extends View & Checkable
in the actual case does not mean that you can return any type that extends View
and implements Checkable
.
It means that the client can cast the returned type to any type that extends View
and implements Checkable
.
A client could do something like this:
class CheckableView extends View implements Checkable {}
class OtherView extends View implements Checkable {}
private static <T extends View & Checkable> T get() {
return (T) checkableView;
}
OtherView otherView = Main.get();
CheckableView checkableView = Main.get();
but what do you want to do now with the implementation?
private <T extends View & Checkable> T get() {
return ...;
}
What should this method return? If you return a CheckableView
the compiler will say that you must cast it to T
, because a client might cast it to any type T
. But this also means that you will get an Unchecked cast from CheckableView to T warning, because if the client casts it to any other type than CheckableView
, e.g. OhterView
, you will get a ClassCastException
.
Since the get
method implementor can not know to which type the client will ever cast it, he can not determine which object to return.
And if the the implementor returns a specific object (e.g. CheckableView
), than the client can only cast it to that type. So why does the implementor not change the method signature to return exactly that type? .... or introduce a special type or interface for this case?
Maybe this is a solution for you
Create an interface that the get
method will return, e.g.
public interface CheckableView {
}
Change the get method to return an CheckableView
private CheckableView get() {
}
Write an adapter that adapts your View
s that implement Checkable
to the CheckableView
public class CheckableViewAdapter<T extends View & Checkable> implements CheckableView {
private T checkableView;
public CheckableViewAdapter(T checkableView) {
this.checkableView = checkableView;
}
}
Now you can create instances of a CheckableView
for every View
that implements Checkable
class SomeView extends View implements Checkable {}
SomeView someView = ...;
CheckableView checkableView = new CheckableViewAdapter<SomeView>(someView);
Add the methods that a client of the get
method needs to the CheckableView
interface and implement it in the CheckableViewAdapter
.
public interface CheckableView {
public void bringToFront();
}
public class CheckableViewAdapter<T extends View & Checkable> implements CheckableView {
private T checkableView;
public CheckableViewAdapter(T checkableView) {
this.checkableView = checkableView;
}
public void bringToFront(){
checkableView.bringToFront();
}
}
or just
public interface CheckableView {
public View getView();
public Checkable getCheckable();
}