Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are implicit generics in classes not allowed in Java? [closed]

Tags:

java

generics

I am trying to understand why Java does not allow using implicit types in generic classes as it does in generic methods.

I have scoured the webs for answers on this, but have yet to come across a reason as to why something like the following is not supported in Java:

// This will not compile:
public <T> interface ElementPicker<L extends List<T>> { ... }

// This on the other hand will
public interface ElementPicker<T, L extends List<T>> { ... }

And so we must explicitly mention the type T in the class generic arguments. This of course means that we must now always write:

ElementPicker<Integer, List<Integer>>
// instead of just:
ElementPicker<List<Integer>>

This leads to a constant head-ache in my code where I am attempting to balance using generics wisely, while making my types readable and short.

Unfortunately, in my current project I am dealing with a bunch of nested generic types, and their type arguments are bubbling to the top so that I have very long top-level classes that must include all generic type data.

To see how this can become a problem consider:

interface ScalarValue<T extends Number> {
  T toNumber();
}

interface ScalarAdder<T, U extends ScalarValue<T>> {
  T add(U v1, U v2);
}

class ScalarAccumulator<T, U extends ScalarValue<T>, 
                           A extends ScalarAdder<T, U>> {
  ...
}

// Assuming we have these implementations:
class RationalValue implements ScalarValue<Float> { .. }
class RationalAdder implements ScalarAdder<Float, RationalValue> { .. }

// A typical type could look like this monster:
ScalarAccumulator<Float, RationalValue, RationalAdder>

// Whereas an implicit declaration like this:
public <T extends Number, 
        U extends ScalarValue<T>,
        A extends ScalarAdder<T, U>
class ScalarAccumulator<A> { ... }

// ... would allow us to simply write
ScalarAccumulator<RationalAdder>
// ...as all other types can be inferred.

Again this is an example, but I encounter these kinds of things quite often in my work. And I have yet to find a reason for why this is not possible. Methods can this just fine (infer types from a single value and its class).

So why can Java support this in methods but not in classes? I cannot think of an example where it would be a problem for a class to not explicitly include the type. But I am probably missing something.

If anybody has any good tips on work-arounds to deal with such situations in general, I would also appreciate it.

like image 847
Marius Renn Avatar asked Jan 11 '19 03:01

Marius Renn


People also ask

Why primitive data types are not allowed in Java generics?

Using generics, primitive types can not be passed as type parameters. In the example given below, if we pass int primitive type to box class, then compiler will complain. To mitigate the same, we need to pass the Integer object instead of int primitive type.

Why generics are introduced in Java What problems do they overcome?

To overcome the above problems of collections(type-safety, type casting) generics introduced in java 1.5v . Main objectives of generics are: 1) To provide type safety to the collections. 2) To resolve type casting problems. To hold only string type of objects we can create a generic version of ArrayList as follows.

How can we restrict generic to a subclass of particular class?

Whenever you want to restrict the type parameter to subtypes of a particular class you can use the bounded type parameter. If you just specify a type (class) as bounded parameter, only sub types of that particular class are accepted by the current generic class.


1 Answers

In Java 10 you can use var to skip type declarations. Here's a (somewhat hacky) way to combine that with type inference so you can create an instance without declaring all the nested types:

static <T, U extends ScalarValue<T>, A extends ScalarAdder<T, U>> ScalarAccumulator<T, U, A> create(Class<A> adderClass) {
    return new ScalarAccumulator<>();
}

static void test() {
    var a = create(RationalAdder.class);
}
like image 143
shmosel Avatar answered Nov 10 '22 00:11

shmosel