Redundant generic parameters
Asked Answered
R

6

9

I have this two interfaces and classes:

public interface Identifiable<T> {
    T getId();
}

public interface GenericRepository<T extends Identifiable<K>, K> {
    T get(K id);
}

public class MyEntity implements Identifiable<Long> {

    private Long id;

    public Long getId() {
        return id;
    }
}

public class MyService {
    private GenericRepository<MyEntity, Long> myEntityRepository;
}

It all works as desired. But in my opinion second generic parameter in GenericRepository (K) is redundant. Because I know that MyEntity is an Identifiable, I think it would be great if I can finally use it like this:

public class MyService {
    private GenericRepository<MyEntity> myEntityRepository;
}

But I'm trying different things without succeeding. Is it possible? If not, why not?

UPDATE: Answering some responses. I think compiler knows something about which type is the generic in MyEntity. For example:

public class MyEntityGenericRepository implements GenericRepository<MyEntity, Long> {
    // compiles...
}

public class MyEntityGenericRepository implements GenericRepository<MyEntity, String> {
    // compiler says: "Bound mismatch: The type MyEntity is not a valid substitute for the bounded parameter <T extends Identifiable<K>> of the type GenericRepository<T,K>"
}
Reduce answered 13/3, 2012 at 12:47 Comment(0)
M
7

I don't think you can omit it. With T extends Identifiable<K> you want to say that the generic type parameter must be an Identifiable. Since Identifiable is a generic class, you need to mention its generic type parameter too (if you want to play by the rules that is - if you omit it, you lose all generic type safety for GenericRepository, due to backward compatibility rules). Note also that K is actually used as the parameter type of GenericRepository.get. And since that type may be different from T, you need to satisfy the compiler by declaring it as another generic type parameter of GenericRepository. Otherwise the compiler has no way of knowing what K is.

Marginate answered 13/3, 2012 at 12:56 Comment(1)
@sinuhepop, of course the compiler can check that the actual type of K matches that of the type parameter in Identifiable<K>. Still, you need to declare K to be a generic type parameter (and consequently, pass its actual value at the point of instantiation). This is just how the language works. You are right that this knowledge could in theory be inferred by the compiler, but it is not for now, and for many years yet... if ever (unfortunately, the development of Java is moving only very slowly to that direction...).Waxman
A
3

It's not redundant from the standpoint of the GenericRepository class. When it has methods like T get(K id), it can't know which types of the id argument it can accept otherwise. You can write the following:

interface GenericRepository<T extends Identifiable<?>> {
    T get(Object id);
}

Now you don't have to write Long as a type parameter, but you lose the possibility to check if the get method is used properly at compile time. So the type variable serves a specific purpose.

And as for the field declaration, when you have a generic type, you have to specify all the type variables it uses. Of course, you could argue that it would be neat if the language could understand that one of the parameter values can be inferred from the other one, but it is debatable.

Attack answered 13/3, 2012 at 12:56 Comment(2)
Please look at my edition. It seems that the compiler can infer the type somehow.Reduce
@sinuhepop, no, it is not inferring any type there, only comparing the equality of two concrete, defined type parameters. (A simple example of) type inference is this (implemented in Java 7).Waxman
P
1

Not much you can do, other than introduce an interface that just refines GenericRepository

  public interface LongKeyedRepository<T extends Identifiable<Long>> 
        extends GenericRepository<T, Long> { {
  //No new methods need to be defined
  }

Then you can have

private LongKeyedRepository<MyEntity> myEntityRepository;

etc.

Ping answered 13/3, 2012 at 13:15 Comment(1)
Thanks. Currently I'm doing something similar to this (I've simplified my structure a little). But I don't like it too much, and I don't understand why is needed.Reduce
M
0

If I am not mistaken generics will all be compiled as if they were just Object. Now the syntax is (hard) checked to ensure that you are not putting apple with oranges because generics were added after the initial design of Java. this is why generics are so constrained...

Maffick answered 13/3, 2012 at 12:58 Comment(0)
C
0

It would work with (i.e. compile)

public interface GenericRepository<T extends Identifiable> {
    T get(T id);
}

but it says nonetheless that Identifiable is a raw type and that it should be parametrized.

Hope it helps.

Chalcis answered 13/3, 2012 at 13:2 Comment(4)
This isn't quite the same - you've declared get as taking an argument of type T rather than K. Arguably that's the crux of the question, how to have a K reference within the class without having to declare it when constructing, so it's a bit cheeky to sidestep it. :)Ketene
I would say it is not possible to have a K reference within the class without having to declare it when constructing. THe only other thing I could think of would be public interface GenericRepository<T extends Identifiable<? extends Object>> { T get(T id); }Chalcis
Yep, that's the question: "Is it possible? If not, why not?"Ketene
Because in Java you can not use something that was not declared. Note that if you try public interface GenericRepository<T extends Identifiable<K extends Object>> { T get(T id); } it would not compileChalcis
D
-1

Why you cannot remove second K from,

public interface GenericRepository<T extends Identifiable<K>, K> {

So rather than having it as above, can we have it as

public interface GenericRepository<T extends Identifiable<K>> {

By this we can do what you want to do.

Downrange answered 13/3, 2012 at 12:59 Comment(1)
Yes, but unfortunately compiler says "K cannot be resolved to a type".Reduce

© 2022 - 2024 — McMap. All rights reserved.