Java Generics: Generic type defined as return type only

62,392

Solution 1

The method returns a type of whatever you expect it to be (<X> is defined in the method and is absolutely unbounded).

This is very, very dangerous as no provision is made that the return type actually matches the returned value.

The only advantage this has is that you don't have to cast the return value of such generic lookup methods that can return any type.

I'd say: use such constructs with care, because you lose pretty much all type-safety and gain only that you don't have to write an explicit cast at each call to get().

And yes: this pretty much is black magic that blows up at runtime and breaks the entire idea of what generics should achieve.

Solution 2

The type is declared on the method. That's that "<X>" means. The type is scoped then to just the method and is relevant to a particular call. The reason your test code compiles is that the compiler tries to determine the type and will complain only if it can't. There are cases where you have to be explicit.

For example, the declaration for Collections.emptySet() is

public static final <T> Set<T> emptySet()

In this case, the compiler can guess:

Set<String> s = Collections.emptySet();

But if it can't, you must type:

Collections.<String>emptySet();

Solution 3

I was just trying to figure out the same thing with a GXT class. Specifically I was trying to call a method with the signature of:

class Model {
    public <X> X get(String property) { ... }
}

To call the above method from your code and have it cast X to a String I do the following:

public String myMethod(Data data) {
    Model model = new Model(data);
    return model.<String>get("status");
}

The above code will call the get method and tell it that the type being returned by X should be returned as a String.

In the case where the method is in the same class as you, I've found that I have to call it with a "this.". For example:

this.<String>get("status");

As others have said, this is rather sloppy and dangerous by the GXT team.

Solution 4

Interesting note, from RpcMap (GXT API 1.2)

get's header:

public java.lang.Object get(java.lang.Object key)

Having a generic parameter of <X> in there that's uninstantiated has the same effect, except you don't have to say "Object" all over the place. I agree with the other poster, this is sloppy and a bit dangerous.

Solution 5

BaseModelData raises unchecked warnings when compiled, because it is unsafe. Used like this, your code will throw a ClassCastException at runtime, even though it doesn't have any warnings itself.

public String getExpireDate() {
  return  get("expiredate");
}
Share:
62,392
Bikash Deka
Author by

Bikash Deka

Updated on July 05, 2022

Comments

  • Bikash Deka
    Bikash Deka almost 2 years

    I'm looking at some GXT code for GWT and I ran across this use of Generics that I can't find another example of in the Java tutorials. The class name is com.extjs.gxt.ui.client.data.BaseModelData if you want to look at all of the code. Here are the important parts:

    private RpcMap map;
    
    public <X> X get(String property) {
      if (allowNestedValues && NestedModelUtil.isNestedProperty(property)) {
        return (X)NestedModelUtil.getNestedValue(this, property);
      }
      return map == null ? null : (X) map.get(property);
    }
    

    X is defined nowhere else in the class or anywhere in the hierarchy, and when I hit "go to declaration" in eclipse it just goes to the <X> in the public method signature.

    I've tried to call this method with the following two examples to see what happens:

    public Date getExpiredate() {
        return  get("expiredate");
    }
    
    public String getSubject() {
        return  get("subject");
    }
    

    They compile and show no errors or warnings. I would think at the very least I would have to do a cast to get this to work.

    Does this mean that Generics allow a magic return value that can be anything and will just blow up at runtime? This seems counter to what generics are supposed to do. Can anyone explain this to me and possibly give me a link to some documentation that explains this a little better? I've went through Sun's 23 page pdf on generics and every example of a return value is defined either at the class level or is in one of the parameters passed in.

  • Bill Michell
    Bill Michell over 15 years
    This construct is much safer when the type is also used to qualify a generic type in one of the methods - which means you don't need to explicitly cast. Then, there is no chance of blowing up at runtime.
  • Bikash Deka
    Bikash Deka over 15 years
    Ok I gave this a shot: It only throws an exception if the runtime type is not of type string. If it is a string it's ok. You've just moved the cast to the get method.
  • brady
    brady over 15 years
    Exactly. If you compile your code without warnings, generics guarantees that you'll never get a ClassCastException; by ignoring the warnings, BaseModelData has thrown away that assurance.
  • David Roussel
    David Roussel over 14 years
    why is that better? It can still blow up at runtime, just in a different way.
  • Newtopian
    Newtopian about 14 years
    Blow-up protection by inserting two successive runtime bombs !! First the cast will blow up as exception..., then, if that were not enough the assert (which would probably not get run anyway) will blow up the entire program !, more complexity... no added benefits... -1
  • Nicole
    Nicole over 13 years
    In the case of GWT it's possible that this was by design; GWT compiles to JavaScript which as we all know is dynamic, weakly typed.
  • Tatarize
    Tatarize over 8 years
    public <T> T get(int i, Class<T> type) { Object val = get(i); if (type.isInstance(val)) { return type.cast(val); } return null; }
  • Joachim Sauer
    Joachim Sauer almost 6 years
    Side note: recent versions of Java (8+) have become smarter with type deduction and examples like the last one no longer need an explicit type (depending on where they are used).