Why does the compiler state no unique maximal instance exists?

20,891

Solution 1

It does not compile because your code expects too much from generics -> i.e., the < X > X part in:

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

In the following code:

public T getObject() {
  return get(OBJECT);
}

you have to keep in mind that generics are always "unfolded" before the compiler actually starts to compile the Java code. It is a pre-processing step.

In your case, the compiler does not know what to use to replace X at compile time. The compiler needs to be sure about the type of X, because it needs to check it against T to validate the code. Hence the error...

A solution to your issue is to replace < X > X with Object:

public Object get(String property) { ... }

and add a cast in:

public T getObject() {
  return (T) get(OBJECT);
}

Your will get an unchecked-cast warning at compile time, but your code will compile (so yes your workaround is valid).

Solution 2

This is dummy bug that has been fixed in Java SE 7.

Solution 3

Method type parameters are most often implicitly inferred from the arguments to that method. Note, however, get has no explicit relationship between the argument and the type parameter:

public <X> X get(String property)

Type inference is the usual path, but methods can also be invoked with explicit type arguments, just like classes. The format roughly follows that of the declaration, so inside of Obj you could have

public T getObject() {
    return super.<T>get(OBJECT);
}

You could also just be direct and use <Object>, but you'd still have to use that unchecked cast to get it back to T. Note the explicit argument needs a qualifier, usually the instance name of the class. Since your example used a method of the superclass, its reference is implicit through super.

This doesn't solve the underlying problem of applying a generic method (<X> X get) inside of a non-generic class (BaseModel). Note the code in the library makes forcible type casts to the type argument. This style is indeed one of the solutions to back-porting generic features into non-generic Java code. It looks like they're trying to hide this from the library users, but since they didn't genericize the class the type can't be inferred from the instance (i.e. you really want to have Obj<T> extends BaseModel<T>).

[EDIT: corrected and explained explicit method type argument]

Solution 4

I just encountered a similar issue with a project using Apache Pivot. The client code was riddled with lines like:

boolean foo = org.apache.pivot.json.JSON.get(item, "foo");

The code would compile in Eclipse, but not using Maven or javac from the command line. It appears to be Bug 6302954, but I still see it after updating to the latest JDK.

As the JSON class is provided by Pivot, it's not something I could modify within my own source tree (forking the library is not an option on this project)

The solution that worked for me came from the first reply in the bug report, changing the code to read:

boolean foo = org.apache.pivot.json.JSON.<Boolean>get(item, "foo");
Share:
20,891

Related videos on Youtube

Snekse
Author by

Snekse

'Foodie for Life' is tattooed across my tummy. Oh, I also do some of that Java programming stuff. SOreadytohelp

Updated on July 09, 2022

Comments

  • Snekse
    Snekse almost 2 years

    I have the following classes:

    public class Obj<T> extends BaseModel {
    
        public static final String OBJECT = "object";
    
        public Obj(T object) {
            setObject(object);
        }
    
        public T getObject() {
            return get(OBJECT);
        }
    
        public void setObject(T object) {
            set(OBJECT, object);
        }
    }
    

    And...

    /** This is a 3rd party library class **/
    public class BaseModel implements ModelData, Serializable {
      //...members and stuff...
    
      @SuppressWarnings({"unchecked", "rawtypes"})
      public <X> X get(String property) {
        X obj = null;
        if (start > -1 && end > -1) {
          Object o = map.get(property.substring(0, start));
          String p = property.substring(start + 1, end);
          if (o instanceof Object[]) {
            obj = (X) ((Object[]) o)[Integer.valueOf(p)];
          } else if (o instanceof List) {
            obj = (X) ((List) o).get(Integer.valueOf(p));
          } else if (o instanceof Map) {
            obj = (X) ((Map) o).get(p);
          }
        } else {
          obj = (X) map.get(property);
        }
        return obj;
      }
    }
    

    When I compile, I get the following error.

    type parameters of <X>X cannot be determined; no unique maximal instance exists for type variable X with upper bounds T,java.lang.Object -> getObject()

    It doesn't happen in Eclipse, which, as far as I can tell, is using the same JDK as my Ant build. I've seen the SO thread about the Sun compiler issue, but that seemed to be for static methods declaring types on the fly.

    Why am I getting this error, and more importantly, how do I get around it?

    So far the only why I've found is to cast in my method like this:

    @SuppressWarnings({"unchecked"})
    public T getObject() {
        return (T) get(OBJECT); //yuck
    }
    

    Telling my I'm on crack and this is the proper way is acceptable.

    • Paŭlo Ebermann
      Paŭlo Ebermann about 13 years
      Another way to do this would be to provide the method call with the type parameter: return get<T>(OBJECT);
    • Paŭlo Ebermann
      Paŭlo Ebermann about 13 years
      You are right, the right syntax is `return <T>get(OBJECT);'. Sorry for mixing.
  • Snekse
    Snekse about 13 years
    The public <X> X get(String property) method is part of a 3rd party library that I'm unable to change.
  • Snekse
    Snekse about 13 years
    The code return <T> get(OBJECT); does not compile. Did you mean to cast?
  • Jérôme Verstrynge
    Jérôme Verstrynge about 13 years
    Ok, I get it (this library is really not written very well, lol). Anyway, as you figured out yourself, a cast to (T) in getObject() alone solves the compilation issue too. There is - unfortunately - no other option here...
  • Jérôme Verstrynge
    Jérôme Verstrynge about 13 years
    In fact, this library looks like a bad attempt at implementing an heterogeneous container...
  • Chad Wellington
    Chad Wellington about 13 years
    Whoops, I forgot to give the qualifying expression. I'll fix that. And no, there really is a difference between a cast and an explicit type argument. Here's some more explanatory material.
  • Guillaume
    Guillaume over 11 years
    But why eclipse is fine (and intellij too) with this kind of code, and compilation done with ant (and maven too) fails ?
  • CHiRo79
    CHiRo79 about 11 years
    It's a solution, but not the best solution. The code is good. Check this link docs.oracle.com/javase/tutorial/java/generics/… The problem maybe is because the compiler is old, or... I don't know.