0

Can, and if yes how, I access the generic parameter of another generic parameter, without also specifying it?

The example below is simplified from my current situation. All members and methods are omitted as they aren't relevant to the situation. LibraryRepository is a JPA/Spring-Type expecting an ENTITY and an ID.

abstract class AbstractEntity<ID> {}

class ConcreteEntity extends AbstractEntity<Long> {}

interface AbstractRepository<ENTITY extends AbstractEntity<?????>> extends LibraryRepository<ENTITY, ?????> {}

//Desired declaration:
interface ConcreteRepository extends AbstractRepository<ConcreteEntity> {}

Without specifying Long on AbstractRepository, what to write instead of the two ?????, if that is at all possible?


For the sake of question-scope, please answer the question regarding generics, not how to implement spring-repositories in a better way. I'm glad for hints about that in the comments though.

3
  • Unrelated: follow the java naming conventions for type parameters, they are typically a single char. Commented Nov 24, 2020 at 10:27
  • 1
    That was for the example. Makes it easier to argue about the types when they have proper names. Commented Nov 24, 2020 at 10:29
  • "Easier" is subjective. Most people are used to follow the java standard, and they will find multi-char types confusing. And worse, language newbies might take them for class names. Commented Nov 24, 2020 at 11:54

2 Answers 2

1

interface AbstractRepository<ENTITY extends AbstractEntity<?????>> extends LibraryRepository<ENTITY, ?????> {}

One (particularly useful) way of thinking about generics is that they link types together. If you declare a new typevar and use it in only one place, because of erasure, that is effectively useless. Use it in two places and now you've told the compiler that the two places you used your T are linked: They can be anything, as long as they are the same thing.

This way of thinking also provides some insight here: Clearly you want your first ????? to be linked to your second ?????: For them to be equal.

The way to do that, then, is to declare a new typevar and use it:

interface AbstractRepository<ENTITY extends AbstractEntity<Q>, Q>
   extends LibraryRepository<ENTITY, Q> {}

Unfortunately, that means AbstractRepository now gained a type variable, and I gather you didn't want that to happen.

Unfortunately, java has no way to link types without a type variable.

As a general rule of thumb, if you mix class hierarchies (things extending things implementing things), and lots of generics, you end up with looooots of generic parameters, and some of those will feel like DRY violations. The solution is to either accept it, or to not do one of those two things (Use composition over inheritance, or reduce the type variables you're using, or move them to methods instead), or to use a bunch of hacky reflection and 'warning-casts' (where you cast things to a typevar, which doesn't actually typecheck anything and tends to result in ClassCastExceptions in bizarre places: Places that don't even have a cast anywhere in the line, and not the place with the faulty code. Resulting in long and arduous bughunting exercises.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the longer explanation. Well, one can't have every cake. This is probably not the worst feature to miss.
1

Maybe this is what you're trying to avoid, but I think you'd specify the generic parameter of your AbstractEntity as a parameter in AbstractRepository, something like this:

interface LibraryRepository<ENTITY, ID> {}

abstract class AbstractEntity<ID> {}

class ConcreteEntity extends AbstractEntity<Long> {}

interface AbstractRepository<ENTITY extends AbstractEntity<ID>, ID>
          extends LibraryRepository<ENTITY, ID> {}

interface ConcreteRepository extends AbstractRepository<ConcreteEntity, Long> {}

I don't know how otherwise the compiler would be able to type-check the generic parameters for LibraryRepository.

2 Comments

That would be what I'm trying to avoid. I figured that, since ENTITY already knows ID, I might be able to skip redeclaring it when writing the definition of ConcreteRepository.
As far as I know, not possible. Java generics are not that smart or convenient.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.