0

I'm currently learning java, and I encountered a problem with generics that I can not solve.

So I have this class:

public class EntityRepository <Entidade extends Entity>{ "code" } with this method:


public Long create(Entidade ent) {

    ent.setId(this.nextId());
    this.m1.put(ent.getId(), ent);
    return ent.getId();
}

and now i have another method in another class like this:

public static void subMenu(EntityRepository<? extends Entity> repository, String entidade)

which has this code:

Entity item = new Entity();
if(entidade.equals("produto")){
    item = new Product();
}
else if(entidade.equals("prateleira")){
    item = new Shelf();
}
repository.create(item);
repository.printList(entidade);

so basically when String 'entidade' is equal to strings "produto" or "prateleira" it is supose to call the 'create' method from the first class I mentioned above but with either a 'Product' or a 'Shelf' as a parameter. (which are objects that extend to Entity)

my problem is that is that has this error:

"The method create(capture#1-of ? extends Entity) in the type EntityRepository is not applicable for the arguments (Entity)"

and i cant seem to understand why nor can i find any clues around the web...

can some one help me? thanks!

f.khantsis
  • 3,256
  • 5
  • 50
  • 67
  • 1
    replace `EntityRepository extends Entity> repository` with `EntityRepository super Entity> repository` – Iłya Bursov May 10 '17 at 23:00
  • Short answer - because `EntityRepository` is generic, you could be dealing with an `EntityRepository` which means you'd have to pass an `Xyz` to `create`, instead of just a plain `Entity`. – Dawood ibn Kareem May 10 '17 at 23:00
  • 2
    Looks like you shouldn't actually be using generics here, since you expect `create` to accept any type of `Entity` as an argument. – Radiodef May 10 '17 at 23:05
  • 1
    Your code would be a lot easier to follow if you followed the naming convention for generics parameters to consist of a single uppercase letter each, e.g., `EntityRepository`. Using what looks like an actual type name, `Entidade`, as a generics parameter makes it very hard indeed to figure out which the symbol represents at different times - is it a generics parameter or a type? Your cooperation helps everyone (including yourself) help you. – Lew Bloch May 11 '17 at 00:47
  • @Radiodef The problem is not with the `create` method and the `Entity` item it receives, but with the `EntityRepository extends Entity> repository` argument of the `subMenu` method instead. – fps May 11 '17 at 01:10
  • @FedericoPeraltaSchaffner I'm well aware of why this error happens. However, I've looked at the code for `subMenu` and observed that OP intends to pass several different types of `Entity` to `create` such that the only type argument which would fulfill this requirement is equivalent to the upper bound of `Entidade`, which makes the generics pointless. – Radiodef May 11 '17 at 01:35
  • 1
    @Radiodef Yes, I agree with you on this. – fps May 11 '17 at 01:47

2 Answers2

1

First of all, you should stick to naming conventions regarding generic type parameters, which state that they should consist of a single capital letter. So let's change your code accordingly:

public class EntityRepository<E extends Entity> {
    // code
}

Now, your problem consists of a misunderstanding of the PECS principle. In short, PECS stands for Producer Extends Consumer Super.

It means that if a parameterized type, i.e. EntityRepository produces a type that matches the type of its parameter, i.e. Entity, you should use the keyword extends. By produces I mean that one or more methods of the parameterized type return a value whose type is the type of the generic parameter. In your example, in the subMenu method, if EntityRepository had a method that returns a descendant of Entity (which is its generic type parameter), then it would be a producer.

On the contrary, if a parameterized type, i.e. EntityRepository consumes a type that matches the type of its parameter, i.e. Entity, you should use the keyword super. By consumes I mean that one or more methods of the parameterized type receive one or more arguments whose type is the type of the generic parameter. In your example, in the subMenu method, if EntityRepository had a method that accepts a descendant (yes, a descendant!) of Entity (which is its generic type parameter), then it would be a consumer.

Now, within the subMenu method, you are invoking the EntityRepository.create method, which receives an Entity as an argument, so, according to the PECS principle, the signature of your subMenu method should be:

public static void subMenu(EntityRepository<? super Entity> repository, String entidade)

which means that, within the scope of the subMenu method, EntityRepository is a consumer of Entity.

Community
  • 1
  • 1
fps
  • 33,623
  • 8
  • 55
  • 110
  • 1
    This answer is perfectly fine but the use of `? super Entity` just illustrates what I was talking about in my other comments. Our upper bound of `E` is declared as `Entity` and now we've bounded it with a lower bound of `Entity` as well, so there is only one type which fulfills both constraints: `Entity`. In other words, `subMenu` can only ever be called with an `EntityRepository`. – Radiodef May 11 '17 at 01:58
  • first of all thanks for all the comments. really :D and second, ill try to make my code better with the upper case letters. now im pretty sure that i want to use `extends` and not `super` cause I beleave its the same as`integer` extends to `number` as my `entity` would be same as `number`. when i rename to `? super Entity`, my `subMenu` calls will give an error as there is nothing superior to `Entity`, except maybe `Object`. – Nuno moreira May 12 '17 at 13:46
  • @Nunomoreira Well, if I use `EntityRepository extends Entity> repository` the code does not compile, specifically the line `repository.create(item);` in the `subMenu` method throws an error. When I change it to `EntityRepository super Entity> repository`, everything works fine. However, when you try to invoke your `subMenu` method with i.e. an `EntityRepository` the code does not compile. And it's fine that it doesn't, because you might be calling `subMenu` with an `EntityRepository` and the string `"prateleira"`. – fps May 12 '17 at 17:03
  • @Nunomoreira If you did that, inside the `subMenu` method, you would end up calling `create` on the passed `repository` (which is an `EntityRepository`) with an argument of type `Shelf`. This is why the compiler complains. So IMHO all this indicates that you should carefully rethink your design. Question... Why aren't both `Entity` and `EntityRepository` abstract? Besides, why are you creating the concrete entity outside the repo? i.e. in your `subMenu` method. And most important, why are you creating the concrete entity based on a string parameter? – fps May 12 '17 at 17:04
0

Thank you all! the prolem was solved. It was my fault as i didnt described it as i should. im new to this xD

anyway thanks!