4

I have a problem about passing an parameter to a generic method. The code is the following:

public class View<T extends View<T,PM>, PM extends Source>  {

    protected PM source;
    protected EventManager<T, PM> eventManager;

    public View(PM s){
        this.source = s;
        eventManager = new EventManager<T, PM>();
        eventManager.setTarget(this); //error: "The method setTarget(T) in the type
                              //EventManager<T,PM> is not applicable for the arguments (View<T,PM>)"

        eventManager.setSource(s);
    }

    public void setBinding(Topic topic, IEventAction<T,PM> action){
        eventManager.setEventAction(topic, action)
    }

}

/** 
* EventManager class has to remain completely generic. The type parameters cannot "extends"
* anything because the EventManager is used also in other parts where T and S will have to be
* classes other than "View" and "Source"
*/
public class EventManager<T, S> {
    protected T target;
    protected S source;
    private LinkedHashMap<Topic, IEventAction<T, S>> eventActions;

    public EventManager(T target, S source){
        this.target = target;
        this.source = source;
    }

    public void setTarget(T target){
        this.target = target;
    }

    public void setSource(S source){
        this.source = source;
    }

    public void setEventAction(Topic topic, IEventAction<T, S> action) {
        //some code here ...
        omissis...

        eventActions.put(topic, action);

        omissis...
    }

    //other methods down here...omissis
}

Eclipse gives me the error I put in comment on the line "eventManager.setTarget(this);". I can't understand why it gives me this error. Anyway, I found a solution (apparently) but I am not sure whether I did a "clean" or "dirty" thing. The solution is this:

 eventManager.setTarget((T)this);

but it gives me a warning: "Type safety: Unchecked cast from View to T". To elimitate the warning I also put the following on top of the constructor method:

@SuppressWarnings("unchecked")

It seems working but what is wrong? Do you have another "cleaner" solution (if it exists)? Do you think this is a "dirty" approach?

Any soggestions are very welcome.

5
  • On a side note, letting the reference to " this" from constructor isn't a good practice, more so in concurrent context. Commented Dec 7, 2011 at 15:46
  • I don't know if you read my answer, but it's possible that you are using generics for no reason at all. I don't have all the details of what you are doing with your classes, so I'm not sure if it's the case. Commented Dec 7, 2011 at 18:16
  • @Scorpion In principle I absolutely agree on what you said about the usage of "this". But in my particular case it is used just to assign a reference to EventManager. Nothing else will use that reference before the "View" object is fully created. By the way, the only object which will use EventManager is the View itself. For this reason, I am in a safe situation. Commented Dec 8, 2011 at 7:37
  • @toto2 From the example above it appears superfluous to use the Generics because I omitted some details of the class View. In particular, it has a parameterized method whose parameter must be of the type "SomeClass<T,PM>". That is why I need the generics in my case. I will try to post the complete classes definition so that it will appear clearer. Commented Dec 8, 2011 at 7:50
  • It looks like not only EventManager should not be parametrized, but also that IEventAction should not be. Commented Dec 8, 2011 at 14:10

6 Answers 6

4

The error occurs because you are instantiating EventManager with T, which can be any subclass of View at runtime, but you are passing exactly View (known at compile time). Since normally you cannot pass a superclass when a subclass is required, your code does not compile.

The solution (without changing the code) is of course to cast the superclass to the subclass (which is what you are doing) and try your best not to get a ClassCastException.

If you are sure that you will never pass incompatible types, then it's ok I guess (though very confusing). Maybe try to redesign it somehow.

Sign up to request clarification or add additional context in comments.

Comments

3

In the line

public class View<T extends View<T,PM>, PM extends Source>  {

you appear to want T to be "the type of this". But that isn't expressed in the language.

Generally the approach here is to make the class abstract and add an abstract getThis method:

public abstract class View<THIS extends View<THIS,PM>, PM extends Source>  {
    protected abstract THIS getThis();
    ...
        eventManager.setTarget(getThis());
        ...

public final class SomeView extends View<SomeView,SomeSource> {
    protected SomeView getThis() {
        return this;
    }
    ...

1 Comment

That works GREAT! Very Good idea! I like this solution. Thanks a lot Tom and thanks to all of you guys for your support. Of course, if someone has a different idea/comment/suggestion it will be a pleasure to continue discussing.
1

The error is because the event manager is initialized to hold a type of "T" during construction, but you are trying to assign an instance of " View " (T extends View, making View the superclass) which isn 't right.

Comments

0

I agree with what Tudor said but also it is best practice to not suppress unchecked warnings for the method/constructor. This would cause any other unchecked exceptions to be hidden. I suggest doing it for the line you need. Below is the example.

@SuppressWarnings("unchecked")
T t = (T)this;
eventManager.setTarget(t);

3 Comments

The View class has to be extended (actually is an abstract class) like this: public class MyView extends View<MyView, Source>{ public MyView(Source s) { super(s); } } Given the definition of View, it cannot be instantiated differently, for example in this way: "View<String, Source>". The first parameter type must be a subtype of View otherwise the compiler gives an error. So, having said that, at the line "eventManager.setTarget(this)", "this" always represents a subclass of View and I will never get a runtime ClassCastException (I think).
@user1085876 that's a lot of code in a comment. Maybe you should create an answer with that.
I tryed but I can't. I am a newcomer and I can't asnwer to my own question before 8 hours. Anyway, it is just a simple subclass definition extending View and invoking the super constructor. I put all the code in quote "public class MyView extends View<MyView, Source>{ public MyView(Source s) { super(s); } }". Sorry for that.
0

I think to comply with the declaration EventManager should extend View, because T on line eventManager = new EventManager<T, PM>(); is definded as T extends View<T,PM> - naming the type templates the same makes it confusing.

Comments

0

I don't know what you are really doing, but here's something.

Solution 1

public class View<PM extends Source>  {

    protected PM source;
    protected EventManager<View<PM>, PM> eventManager;

    public View(PM s){
        this.source = s;
        eventManager = new EventManager<>(); // diamond notation (Java 7 only)
        eventManager.setTarget(this); 
        eventManager.setSource(s);
    }
}    

You could still have subclasses of View and setTarget would always receive the right expected type.

Solution 2:

EventManager should only take a Source (or subclass) as a source, no?

public class View {

    protected Source source;
    protected EventManager<View> eventManager;

    public View(Source s){
        this.source = s;
        eventManager = new EventManager<>(); // diamond notation (Java 7 only)
        eventManager.setTarget(this); 
        eventManager.setSource(s);
    }
}    

public class EventManager<T> {
   protected T target;
   protected Source source;

   public void setTarget(T t) ...
   public void setSource(Source s) ...
}

Solution 3

You should have a Target class or interface, so that EventManager would not be parametrized at all and View would extend or implement Target. You would be left with no generics at all. If simple inheritance can do the job, there's no need for generics.

Note: as mentioned by Scorpion, you should not escape this in a constructor; you might get some event that triggers an access to View before it has been fully constructed.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.