3

It's confusing because type declaration and usage both use the <T> syntax. I think there are only 2 places where you can declare new generic types in Java:

1. In the definition of a class or interface

class MyClass<T> ...
//           ^^^- Type declaration

interface MyInterface<T,U> ...
//                   ^^^^^- Declaring 2 types 

2. Before a static method

// The declared type must then be used as a parameter of that method...
static <T> void foo(T t...
//     ^^^

// or as the return type:
static <T> T foo(...
//     ^^^

Is that true? Am I using the right words? Should I say "introduce" a type variable instead of "declare?" Things like the following statement worry me:

It is possible for a constructor to be generic independently of whether the class the constructor is declared in is itself generic.

Source: Java Language Specification 8.8.4. Generic Constructors

I'm trying to write up the limits of Java generics, but I'm only just getting started.

  • 4
    The method doesn't have to be static. It's just that instance methods can reuse the declaration from their class, and it's usually a good idea to do so, therefore you rarely see type declarations on them. – Kilian Foth Nov 17 '16 at 14:59
  • @KilianFoth instance methods are often limited by inheriting from a super-class or interface, but yes, you're right for instance methods that don't override any other methods. – GlenPeterson Nov 17 '16 at 15:43
4

Any method including constructors can take type parameters. I have yet to see a case in Java where you'd want a generic constructor, but it's entirely legal to declare something like

class Thingy {
    private final ArrayList<String> items;

    public <A> Thingy(Collection<A> values, Function<A, String> function) {
        items = new ArrayList<>(values.size());
        for (A thing : values)
            items.add(function.apply(thing));
    }
    ...
}

And invoke the constructor as new Thingy(items, x -> x.toString()) when relying on type inference, or as new<Integer> Thingy(items, x -> x.toString()) when specifying the type parameter explicitly. That's right, the constructor type arguments go after the new keyword.

Instance methods may also have type parameters, e.g. I could define

class DataSource<T> {
  ...
  public <R> DataSource<R> transform(Function<T, R> transformation) { ... }
}

When compared with other type systems with type parameters (C++, C#, Haskell, …), Java doesn't have notable restrictions regarding the places where type parameters may or may not be declared. Instead, this type-erasure based system has notable restrictions on how those type parameters may be used, e.g. regarding arrays, catch clauses, primitive types, and the lack of type aliases.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.