Leviand
Leviand

Reputation: 2805

Type problem with generic object argument

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.

  1. In the first case, why the compiler is not aware that my Manager class is extending WebserviceIntegration, which makes it compatibile with F class?

  2. 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

Answers (3)

davidxxx
davidxxx

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 Fdeclaration 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

Maxim
Maxim

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

Matthieu Gabin
Matthieu Gabin

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

Related Questions