1

When writing a type handler for a repository (such as a web service or a database), I need to instantiate the type after the value is loaded from the repository.

Let's say I get a String value from the repository and there is a constructor with one String argument that I can use. If the return type has a type parameter, what else can I do besides instantiating the raw type? It seems raw types exist only for compatibility with legacy code so I would prefer not to use them.

Normally ? can be used as type parameter (if you know the type will be correct at runtime), but not in this case because you can't instantiate classes with wildcards as type parameter.

EDIT: some example code:

Let's say I have a PrimaryKey class like this:

public class PrimaryKey<R extends RepositoryObject<R>> {
    private String value;
    public PrimaryKey(String value) {
        this.value = value;
    }
}

And a set of classes that extend RepositoryObject, which is defined like this:

public class RepositoryObject<R extends RepositoryObject<R>> {
    private PrimaryKey<R> pk;
    public RepositoryObject(PrimaryKey<R> pk) {
        this.pk = pk;
    }
    PrimaryKey<R> getPrimaryKey() {
        return pk;
    }
}

Example of a subclass:

public class User extends RepositoryObject<User> {
    public User(PrimaryKey<User> userId) {
        super(userId);
    }
}

Now the type handling method for class PrimaryKey will look something like this:

public PrimaryKey<?> getValue(String stringValue) {
    return new PrimaryKey<>(stringValue);
}

But this results in a compiler error (in the Maven build, not in Eclipse IDE strangely enough) even though I'm using the diamond operator instead of when instantiating. Maybe for some reason type inference doesn't work well because of the recursion in the type parameters.

Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
herman
  • 11,740
  • 5
  • 47
  • 58

2 Answers2

1

In Java 7 you can typically use the diamond operator to get around this limitation:

Container<?> c = new Container<>(arg);

Otherwise you can use a helper factory method:

<T> Container<T> makeContainer(String arg) {
    return new Container<T>(arg);
}

...

Container<?> c = makeContainer(arg);

EDIT:

Following your update, I can see you're using a recursive type parameter <R extends RepositoryObject<R>>. This compile error is due to limitations of javac when it comes to wildcard capture and recursive type parameters. See this related post for example: Java CRTP and Wildcards: Code compiles in Eclipse but not `javac`

Unfortunately, using a raw type is necessary as a workaround, but it can be hidden as an implementation detail:

public PrimaryKey<?> getValue(String stringValue) {
    @SuppressWarnings("rawtypes") //a raw type is necessary to placate javac
    final PrimaryKey<?> pk = new PrimaryKey(stringValue);
    return pk;
}
Community
  • 1
  • 1
Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
  • I had tried this before (just using > as a type parameter) but it is illegal to instantiate using a wildcard parameter. Eclipse doesn't report a compilation error, but my maven build does. Also see http://stackoverflow.com/questions/12200136/cannot-instantiate-type-in-generics. – herman Apr 02 '13 at 16:39
  • I'm not sure I understand your comment. Did you try either solution? Note the difference between `new Container<>(arg)` and `new Container>(arg)`. – Paul Bellora Apr 02 '13 at 16:46
  • I had tried it before using the diamond operator (<>) and while Eclipse didn't show an error, I did get a compiler error because I'm trying to instantiate a generic type with a wildcard. I'll add a bit of code in my question. – herman Apr 02 '13 at 16:56
  • Works for me [here](http://ideone.com/1wSExv) (sun-jdk-1.7.0_10). What compiler is Maven using? – Paul Bellora Apr 02 '13 at 17:02
  • Okay, so recursive type parameters were what was missing from the picture. See my update. – Paul Bellora Apr 02 '13 at 17:35
  • So it seems like raw types aren't only necessary for backwards compatibility. Oracle should fix this IMHO. Thanks! – herman Apr 03 '13 at 08:48
0
class SomeBogusClass extends RepositoryObject<SomeBogusClass> { }

return new PrimaryKey<SomeBogusClass>(stringValue);

seriously, you can put anything there that satisfies the bounds, even some bogus class that has nothing to do with your code.

newacct
  • 119,665
  • 29
  • 163
  • 224
  • Nope, doesn't work. Not even Object, which doesn't extend RepositoryObject anyway so couldn't possibly be legal. – herman Apr 03 '13 at 08:39