Reputation: 2805
I have an abstract class,
public abstract class WebserviceIntegration {
//...
}
which is used in many classes, like this
public class Manager extends WebserviceIntegration {
//...
}
Now I've built a class, that gets as generic argument the abstract class as F
argument, that has a method inside of it:
public class NewUserWindow<T, F extends WebserviceIntegration> extends Window {
public NewUserWindow(T objOne, F objTwo) {
//...
saveConnectedBean(objTwo); //no problems here
}
public void saveConnectedBean(F bean) throws MyException{
//...
}
}
Ofc everything is fine if I create a new window NewUserWindow<?, Manager> window = new NewUserWindow<>(new OtherClass(), new Manager());
and i save the bean passed to the window.
Now what I wanna do is saving a new Manager
inside the constructor:
public NewUserWindow(T objOne, F objTwo) {
//...
Manager manager = new Manager();
//... setting manager stuff
saveConnectedBean(manager); //does not compile
}
The compiler now stopping me saying that saveConnectedBean(F) in NewUserWindow cannot be applied to (my.package.path.Manager)
.
If I change that with
saveConnectedBean((F)manager);
It compiles, but warns me that the cast is unchecked.
In the first case, why the compiler is not aware that my Manager
class is extending WebserviceIntegration
, which makes it compatibile with F
class?
In the second case, how can I make a safe cast? I can't check with manager instanceof F
, so how can I do?
Thanks
Upvotes: 1
Views: 86
Reputation: 131396
It is not very clear but according to your issue I suppose that the code that fails is located in the constructor of the generic class.
1) The compiler considers your invocation regarding the F
declaration as inside the generic class the compiler doesn't know the exact type of F
. Don't forget that generics are just compilation artifacts.
So if inside the constructor you instantiate your Manager
variable such as :
Manager manager = new Manager();
but you declared your generic type such as NewUserWindow<?, OtherSubClass> window = ...
it will break the type safety.
2) Avoid instanceof
in generic class. It defeats the generic purpose in a some way.
As alternative you should pass the Manager
from the client side of the generic instance such as :
Manager manager = new Manager();
NewUserWindow<?, Manager> newUserWindow = new NewUserWindow<>(new ..., manager);
newUserWindow.saveConnectedBean(manager);
From your comment :
Let's say I can't run across solution 2, is there any other way for safe casting?
You could do that by doing NewUserWindow
an abstract class that declare as abstract a method that returns F
.
public abstract class NewUserWindow<T, F extends WebserviceIntegration> extends Window {
public NewUserWindow(T objOne, F objTwo) {
F f = createF();
saveConnectedBean(f);
}
public abstract F createF();
public void saveConnectedBean(F bean) throws MyException{
//...
}
}
And implement it such as :
public ManagerUserWindow extends NewUserWindow<T, Manager> extends Window {
public Manager createF(){
return new Manager();
}
}
Upvotes: 3
Reputation: 1254
Maybe something like that. Add abstract save
method into WebserviceIntegration
public static abstract class WebserviceIntegration {
public abstract void save();
}
Implement this method in each subclass of WebserviceIntegration
public static class Manager extends WebserviceIntegration {
@Override
public void save() { // saving Manager here }
}
So saveConnectedBean
can now accept WebserviceIntegration
public void saveConnectedBean(WebserviceIntegration bean) { }
It should be safe as each subtype of WebserviceIntegration
have to implement save
functionality.
As an alternative, you can introduce new method type parameter
public <B extends WebserviceIntegration> void saveConnectedBean(B bean) { }
In this case saveConnectedBean
is restricted to be implemented only with methods from WebserviceIntegration
as actual subtype isn't known.
Upvotes: 1
Reputation: 952
1) It's not compiling since you only know that F type is extended WebserviceIntegration but it could be any type that extend WebserviceIntegration
So if you have an other class that extend WebserviceIntegration like
public class NoManager extends WebserviceIntegration {
//...
}
And you create an other NewUserWindow<?, NoManager> noManagerWindow = new NewUserWindow<>(new OtherClass(), new NoManager())
you can't do something like noManagerWindow.saveConnectedBean(new Manager())
2) You should not do a cast since you can't be sure F is Type of Manager.
I think what you want to do is just use the type WebserviceIntegration and not using any generic.
Upvotes: 1