简体   繁体   中英

In java, what is the exact meaning of each type parameter of a generic method in a type introduction?

When we define a generic class, in the type introduction, each type parameter acts as a placeholder for each unique type that will be passed to either the class initializer or a class method. This means I must pass at least two different data types to the BoxA class, defined below.

class BoxA<T, S> {
    private T t;
    private S s;

    public void add(T t, S s) {
        this.t = t;
        this.s = s;
    }

    public T getFirst() {
        return t;
    }

    public S getSecond() {
        return s;
    }
}

I have a very difficult time understanding exactly what type parameters mean for generic methods, because they act differently from generic class type parameters for type introduction. For instance:

class Util {
    public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {
        return p1.getKey().equals(p2.getKey()) &&
                p1.getValue().equals(p2.getValue());
    }
}

class Pair<K, V> {

    private K key;
    private V value;

    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public void setKey(K key) { this.key = key; }
    public void setValue(V value) { this.value = value; }
    public K getKey()   { return key; }
    public V getValue() { return value; }
}

I have no idea why the code above works, because if type parameters assume the same role as placeholders as they do for generic class definitions, then the compare method should receive two arguments of differing data types. Instead, each of them receives an argument of type Pair<K, V> which is also a generic class. What I find difficult to understand is the specific role of K and V within public static <K, V> boolean compare(){} . Are they placeholders for individual types of arguments passed into the method? Are they placeholders for type parameters passed to the actual object-type arguments, which is then passed into the method (ie Pair<K, V> : I view this as one unique type: class Pair<K, V> that receives two unique type parameters, K and V )? Or do generic methods require a unique type parameter for every variable they access whether they are passed as arguments to the method or arguments to an object, which is then passed into the method?

It's hard to understand what exactly you're asking and what exactly you are confused about; I'll try answering your questions anyway.

Type parameters are placeholders for actual types, no matter whether you use them on a class or on a method. Note that this is exactly the same idea as with regular value parameters: they are placeholders for actual values that you supply when you call a method.

public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2)

This is a method with two type parameters, K and V , and two value parameters, p1 and p2 .

When you call this method, you'll have to specify actual types for the type parameters, and actual values for the value parameters. In other words: you'll have to specify what types K and V are and you'll have to pass two instances of class Pair for p1 and p2 .

The type parameters are "passed" to the type Pair in this example. So, the declaration of method compare means: I have a method named compare which takes two Pair objects with type parameters K and V . Instead of specifying actual, concrete types, I leave those as parameters. By doing this, you can call compare with any Pair with specific types for K and V .

For example, you can call compare with two Pair<Integer, String> objects. Or two Pair<BigDecimal, Long> objects, etc.

Are they placeholders for individual arguments passed into the method?

They are placeholders for types, just like regular parameters are placeholders for values.

Are they placeholders for type parameters passed to the actual object-type arguments passed into the method(ie Pair<K, V> //I view this as one unique type: class Pair<K, V> that receives two unique type parameters, K and V)?

Yes... (if I understand your question correctly). The method signature means that the method takes two Pair<K, V> objects, where you leave specifying the actual types to use for K and V to the caller of the method.

Or do generic methods require a unique type parameter for every variable they access whether they are passed as arguments to the method or arguments to an object, which is then passed into the method?

I don't understand what you mean by this question.

I works, because compiler does something called type erasure. Basically, during compilation compiler translates generics into non-generic code. Here are docs explaining it in details: https://docs.oracle.com/javase/tutorial/java/generics/erasure.html

First of all, when you say,

When we define a generic class, in the type introduction, each type parameter acts as a placeholder for each unique type that will be passed to either the class initializer or a class method.

That is not true. The type variables do not have to be in types that are passed to a constructor or method. It's perfectly valid for the type variables to not be used in the declaration of any constructor or method at all (though they would be pretty useless in that case), or used only in a return type (rare), or the type could be embedded in a more complicated type that is used in a constructor or method. For example, it's perfectly reasonable for your generic class BoxA<T, S> to have a constructor

public BoxA(Pair<T, S> p1, Pair<T, S> p2) {
    // ...
}

A generic class promises that (and the compiler checks that) the class works correctly no matter what types are substituted for the type variables, and in fact the class instance doesn't need to know what the type argument is; it is guaranteed that the same code works for all types in the bounds. Similarly, a generic method is a method which has some type variables that are specific to that method itself, and not associated with the type variables of the class instance. It promises that (and the compiler checks that) the method works correctly no matter what types are substituted for the type variables, and the method doesn't need to know what the type argument is.

When you create an instance of a generic class, you do new BoxA<Something, SomethingElse>() , or you use a diamond operator ( new BoxA<>() ), and the context must be such that the compiler can infer the type arguments. When you call a generic method, you can also explicitly specify the type arguments: Util.<String, Integer>compare(pair1, pair2) . However, 99% of the time, you would just write Util.compare(pair1, pair2) , and the compiler infers it for you (ie it verifies that there exists some choice of type arguments that makes the code valid). That is one of the cool things about generic methods. In rare cases, the inference will produce something that does not compile, and you have to explicitly specify the type arguments.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM