JavaTutorial之Generics

Generics泛型
编译错误很容易检查出来,但是运行时错误可不是那么容易检查出来,当发生运行时错误时,程序早就不知道运行到哪里去了
generics enable types (classes and interfaces) to be parameters when defining classes, interfaces and methods:泛型使类型(类和接口)在定义类、接口和方法时成为参数

泛型给程序增加了稳定性

Why Use Generics

  1. 编译时更强的类型检查:Java编译器对泛型代码应用强类型检查,并在代码违反类型安全性时发出错误。修复编译时错误比修复运行时错误更容易,因为运行时错误可能很难找到
  2. Elimination of casts
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0);

List<String> list = new ArrayList<String>();
list.add("hello");
String s = list.get(0);   // no cast
  1. 使程序员能够实现通用算法:通过使用泛型,程序员可以实现在不同类型的集合上工作的泛型算法,可以自定义,并且类型安全且易于阅读

Generic Types
A generic type is a generic class or interface that is parameterized over types:将class或者interface参数化,而不关注类型

A Simple Box Class

public class Box {
    private Object object;

    public void set(Object object) { this.object = object; }
    public Object get() { return object; }
}

传入的不能是primitive type
代码一部分可能希望传入Integer,然后得到Integer,但是另一部分代码给传了个String,这样会得到runtime error

A Generic Version of the Box Class

class name<T1, T2, ..., Tn> { /* ... */ }

泛型模板

/**
 * Generic version of the Box class.
 * @param <T> the type of the value being boxed
 */
public class Box<T> {
    // T stands for "Type"
    private T t;

    public void set(T t) { this.t = t; }
    public T get() { return t; }
}

Invoking and Instantiating a Generic Type

Box<Integer> integerBox = new Box<Integer>();
Box<Integer> integerBox = new Box<>();

JDK7之后,可以省略

Type Parameter and Type Argument

 T in Foo<T> is a type parameter  
 the String in Foo<String> f is a type argument

Multiple Type Parameters

public interface Pair<K, V> {
    public K getKey();
    public V getValue();
}

public class OrderedPair<K, V> implements Pair<K, V> {

    private K key;
    private V value;

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

    public K getKey()	{ return key; }
    public V getValue() { return value; }
}

Pair<String, Integer> p1 = new OrderedPair<String, Integer>("Even", 8);
Pair<String, String>  p2 = new OrderedPair<String, String>("hello", "world");
OrderedPair<String, Integer> p1 = new OrderedPair<>("Even", 8);
OrderedPair<String, String>  p2 = new OrderedPair<>("hello", "world");

OrderedPair<String, Box<Integer>> p = new OrderedPair<>("primes", new Box<Integer>(...));

当然了也可以这么使用

Raw Types

public class Box<T> {
    public void set(T t) { /* ... */ }
    // ...
}

Box<Integer> intBox = new Box<>();

Box rawBox = new Box();
Box<String> stringBox = new Box<>();
Box rawBox = stringBox;               // OK

Box rawBox = new Box();           // rawBox is a raw type of Box<T>
Box<Integer> intBox = rawBox;     // warning: unchecked conversion

Box<String> stringBox = new Box<>();
Box rawBox = stringBox;
rawBox.set(8);  // warning: unchecked invocation to set(T)

不建议使用raw type

Generic Methods

public 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());
    }
}

public 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; }
}
Pair<Integer, String> p1 = new Pair<>(1, "apple");
Pair<Integer, String> p2 = new Pair<>(2, "pear");
boolean same = Util.<Integer, String>compare(p1, p2);

Pair<Integer, String> p1 = new Pair<>(1, "apple");
Pair<Integer, String> p2 = new Pair<>(2, "pear");
boolean same = Util.compare(p1, p2);

Bounded Type Parameters
目的是想对类型参数做出一定的限制,比如想让它是Number的实例或者是子类

public class Box<T> {

    private T t;          

    public void set(T t) {
        this.t = t;
    }

    public T get() {
        return t;
    }

    public <U extends Number> void inspect(U u){
        System.out.println("T: " + t.getClass().getName());
        System.out.println("U: " + u.getClass().getName());
    }

    public static void main(String[] args) {
        Box<Integer> integerBox = new Box<Integer>();
        integerBox.set(new Integer(10));
        integerBox.inspect("some text"); // error: this is still String!
    }
}


Box.java:21: <U>inspect(U) in Box<java.lang.Integer> cannot
  be applied to (java.lang.String)
                        integerBox.inspect("10");
                                  ^
1 error
会报错,需要number类型给的确实string

Multiple Bounds

Class A { /* ... */ }
interface B { /* ... */ }
interface C { /* ... */ }

class D <T extends A & B & C> { /* ... */ }

class D <T extends B & A & C> { /* ... */ }  // compile-time errorA是类,则其必须在前

Generic Methods and Bounded Type Parameters

public static <T> int countGreaterThan(T[] anArray, T elem) {
    int count = 0;
    for (T e : anArray)
        if (e > elem)  // compiler error
            ++count;
    return count;
}

会报错,因为>只能用于基本类型的操作

public interface Comparable<T> {
    public int compareTo(T o);
}
public static <T extends Comparable<T>> int countGreaterThan(T[] anArray, T elem) {
    int count = 0;
    for (T e : anArray)
        if (e.compareTo(elem) > 0)
            ++count;
    return count;
}

利用Comparable

Generics, Inheritance, and Subtypes

Object someObject = new Object();
Integer someInteger = new Integer(10);
someObject = someInteger;   // OK

public void someMethod(Number n) { /* ... */ }

someMethod(new Integer(10));   // OK
someMethod(new Double(10.1));   // OK

Box<Number> box = new Box<Number>();
box.add(new Integer(10));   // OK
box.add(new Double(10.1));  // OK

在这里插入图片描述
Integer是Number的子类,但是Box和Box没关系

Generic Classes and Subtyping

Subtyping 是由extends,implement关键字决定的

ArrayList<E> implements List<E>, and List<E> extends Collection<E>. So ArrayList<String> is a subtype of List<String>, which is a subtype of Collection<String>

在这里插入图片描述

interface PayloadList<E,P> extends List<E> {
  void setPayload(int index, P val);
  ...
}

PayloadList<String,String>
PayloadList<String,Integer>
PayloadList<String,Exception>

在这里插入图片描述
Type Inference

public class BoxDemo {

  public static <U> void addBox(U u, 
      java.util.List<Box<U>> boxes) {
    Box<U> box = new Box<>();
    box.set(u);
    boxes.add(box);
  }

  public static <U> void outputBoxes(java.util.List<Box<U>> boxes) {
    int counter = 0;
    for (Box<U> box: boxes) {
      U boxContents = box.get();
      System.out.println("Box #" + counter + " contains [" +
             boxContents.toString() + "]");
      counter++;
    }
  }

  public static void main(String[] args) {
    java.util.ArrayList<Box<Integer>> listOfIntegerBoxes =
      new java.util.ArrayList<>();
    BoxDemo.<Integer>addBox(Integer.valueOf(10), listOfIntegerBoxes);
    BoxDemo.addBox(Integer.valueOf(20), listOfIntegerBoxes);
    BoxDemo.addBox(Integer.valueOf(30), listOfIntegerBoxes);
    BoxDemo.outputBoxes(listOfIntegerBoxes);
  }
}
The following is the output from this example:

Box #0 contains [10]
Box #1 contains [20]
Box #2 contains [30]
BoxDemo.<Integer>addBox(Integer.valueOf(10), listOfIntegerBoxes);
BoxDemo.addBox(Integer.valueOf(20), listOfIntegerBoxes);

Type Inference and Instantiation of Generic Classes

Map<String, List<String>> myMap = new HashMap<String, List<String>>();

Map<String, List<String>> myMap = new HashMap<>();

Map<String, List<String>> myMap = new HashMap(); // unchecked conversion warning

<>dimand符号不能省去

Type Inference and Generic Constructors of Generic and Non-Generic Classes

class MyClass<X> {
  <T> MyClass(T t) {
    // ...
  }
}

MyClass<Integer> myObject = new MyClass<>("");

Target Types

JDK8之后会自动推断需要的类型

static <T> List<T> emptyList();
List<String> listOne = Collections.emptyList();

Collections.emptyList();返回的是List<T>

the compiler infers that the type argument T must be the value String

void processStringList(List<String> stringList) {
    // process stringList
}

JAVA SE8 之后

processStringList(Collections.emptyList());

也是可以了
之前得

processStringList(Collections.<String>emptyList());

Wildcards通配符

In generic code, the question mark (?), called the wildcard

Upper Bounded Wildcards

List<? extends Number>
extends既可以指代子类,也可以指代实现的接口
List<Number>List<? extends Number> 范围小些后者也可以指代子类
public static double sumOfList(List<? extends Number> list) {
    double s = 0.0;
    for (Number n : list)
        s += n.doubleValue();
    return s;
}
List<Integer> li = Arrays.asList(1, 2, 3);
System.out.println("sum = " + sumOfList(li));
List<Double> ld = Arrays.asList(1.2, 2.3, 3.5);
System.out.println("sum = " + sumOfList(ld));

Unbounded Wildcards

使用场景:

  • 如果您正在编写一个可以使用对象类中提供的功能实现的方法。
  • 当代码在泛型类中使用不依赖于类型参数的方法时For example, List.size or List.clear. In fact, Class<?> is so often used because most of the methods in Class do not depend on T.
public static void printList(List<Object> list) {
    for (Object elem : list)
        System.out.println(elem + " ");
    System.out.println();
}

这个不能打印出来数值相加,只能打印出来对象实例

public static void printList(List<?> list) {
    for (Object elem: list)
        System.out.print(elem + " ");
    System.out.println();
}

List<Integer> li = Arrays.asList(1, 2, 3);
List<String>  ls = Arrays.asList("one", "two", "three");
printList(li);
printList(ls);

Lower Bounded Wildcards

<? super A>.
public static void addNumbers(List<? super Integer> list) {
    for (int i = 1; i <= 10; i++) {
        list.add(i);
    }
}

A 或者A的父类

Wildcards and Subtyping

class A { /* ... */ }
class B extends A { /* ... */ }

B b = new B();
A a = b;

b是A的子类实例,所以上述是成立的

List<B> lb = new ArrayList<>();
List<A> la = lb;   // compile-time error

List<Integer> is not a subtype of List<Numbe

The common parent of List and List is List<?>

?通配符 可以理解为一种规则

在这里插入图片描述
Wildcard Capture and Helper Methods

import java.util.List;

public class WildcardError {

    void foo(List<?> i) {
        i.set(0, i.get(0));
    }
}

WildcardError.java:6: error: method set in interface List<E> cannot be applied to given types;
    i.set(0, i.get(0));
     ^
  required: int,CAP#1
  found: int,Object
  reason: actual argument Object cannot be converted to CAP#1 by method invocation conversion
  where E is a type-variable:
    E extends Object declared in interface List
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Object from capture of ?
1 error

public class WildcardFixed {

    void foo(List<?> i) {
        fooHelper(i);
    }


    // Helper method created so that the wildcard can be captured
    // through type inference.
    private <T> void fooHelper(List<T> l) {
        l.set(0, l.get(0));
    }

}

Type Erasure类型擦除

  • 如果类型参数是无界的,则用其边界或对象替换泛型类型中的所有类型参数。因此,生成的字节码只包含普通类、接口和方法。
  • 如有必要,插入类型强制转换以保持类型安全。
  • 生成桥接方法以在扩展泛型类型中保留多态性。
public class Node<T> {

    private T data;
    private Node<T> next;

    public Node(T data, Node<T> next) {
        this.data = data;
        this.next = next;
    }

    public T getData() { return data; }
    // ...
}

public class Node {

    private Object data;
    private Node next;

    public Node(Object data, Node next) {
        this.data = data;
        this.next = next;
    }

    public Object getData() { return data; }
    // ...
}


public class Node<T extends Comparable<T>> {

    private T data;
    private Node<T> next;

    public Node(T data, Node<T> next) {
        this.data = data;
        this.next = next;
    }

    public T getData() { return data; }
    // ...
}

public class Node {

    private Comparable data;
    private Node next;

    public Node(Comparable data, Node next) {
        this.data = data;
        this.next = next;
    }

    public Comparable getData() { return data; }
    // ...
}

// Counts the number of occurrences of elem in anArray.
//
public static <T> int count(T[] anArray, T elem) {
    int cnt = 0;
    for (T e : anArray)
        if (e.equals(elem))
            ++cnt;
        return cnt;
}

public static int count(Object[] anArray, Object elem) {
    int cnt = 0;
    for (Object e : anArray)
        if (e.equals(elem))
            ++cnt;
        return cnt;
}

class Shape { /* ... */ }
class Circle extends Shape { /* ... */ }
class Rectangle extends Shape { /* ... */ }

public static <T extends Shape> void draw(T shape) { /* ... */ }

public static void draw(Shape shape) { /* ... */ }

Effects of Type Erasure and Bridge Methods

先来看两个类:

public class Node<T> {

    public T data;

    public Node(T data) { this.data = data; }

    public void setData(T data) {
        System.out.println("Node.setData");
        this.data = data;
    }
}

public class MyNode extends Node<Integer> {
    public MyNode(Integer data) { super(data); }

    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }
}
public class Node {

    public Object data;

    public Node(Object data) { this.data = data; }

    public void setData(Object data) {
        System.out.println("Node.setData");
        this.data = data;
    }
}

public class MyNode extends Node {

    public MyNode(Integer data) { super(data); }

    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }
}

经过Type Erasure

public class Node {

    public Object data;

    public Node(Object data) { this.data = data; }

    public void setData(Object data) {
        System.out.println("Node.setData");
        this.data = data;
    }
}

public class MyNode extends Node {

    public MyNode(Integer data) { super(data); }

    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }
}



After type erasure, the method signatures do not match; the Node.setData(T) method becomes Node.setData(Object). As a result, the MyNode.setData(Integer) method does not override the Node.setData(Object) method.
为了解决构造函数类型不匹配的问题:

class MyNode extends Node {

    // Bridge method generated by the compiler
    //
    public void setData(Object data) {
        setData((Integer) data);
    }

    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }

    // ...
}

Bridge Method 取代了原来的构造函数

Non-Reifiable Types不可具体化的类型

Non-reifiable types are types where information has been removed at compile-time by type erasure— invocations of generic types that are not defined as unbounded wildcards. A non-reifiable type does not have all of its information available at runtime

public class ArrayBuilder {

  public static <T> void addToList (List<T> listArg, T... elements) {
    for (T x : elements) {
      listArg.add(x);
    }
  }

  public static void faultyMethod(List<String>... l) {
    Object[] objectArray = l;     // Valid
    objectArray[0] = Arrays.asList(42);
    String s = l[0].get(0);       // ClassCastException thrown here
  }

}

会有 heap pollution

@SafeVarargs
@SuppressWarnings({"unchecked", "varargs"})

可以用这两个注释保证不会出现 ClassCastException你自己得保证

Restrictions on Generics

Cannot Instantiate Generic Types with Primitive Types

class Pair<K, V> {

    private K key;
    private V value;

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

    // ...
    Pair<int, char> p = new Pair<>(8, 'a');  // compile-time error
    Pair<Integer, Character> p = new Pair<>(8, 'a');//autoboxes 
}

Cannot Create Instances of Type Parameters

public static <E> void append(List<E> list) {
    E elem = new E();  // compile-time error
    list.add(elem);
}

public static <E> void append(List<E> list, Class<E> cls) throws Exception {
    E elem = cls.newInstance();   // OK
    list.add(elem);
}

List<String> ls = new ArrayList<>();
append(ls, String.class);

Cannot Declare Static Fields Whose Types are Type Parameters

public class MobileDevice<T> {
    private static T os;

    // ...
}

MobileDevice<Smartphone> phone = new MobileDevice<>();
MobileDevice<Pager> pager = new MobileDevice<>();
MobileDevice<TabletPC> pc = new MobileDevice<>();

Because the static field os is shared by phone, pager, and pc, what is the actual type of os? It cannot be Smartphone, Pager, and TabletPC at the same time.

Cannot Use Casts or instanceof with Parameterized Types

public static <E> void rtti(List<E> list) {
    if (list instanceof ArrayList<Integer>) {  // compile-time error
        // ...
    }
}

这种是不行的,因为type erasure 会将E弄成Object so it cannot tell the difference between an ArrayList<Integer> and an ArrayList<String>

public static void rtti(List<?> list) {
    if (list instanceof ArrayList<?>) {  // OK; instanceof requires a reifiable type
        // ...
    }
}

这种是可以的 ? 通配符的应用

List<Integer> li = new ArrayList<>();
List<Number>  ln = (List<Number>) li;  // compile-time error

类型不同不可转换

List<String> l1 = ...;
ArrayList<String> l2 = (ArrayList<String>)l1;  // OK

相同可转换

Cannot Create Arrays of Parameterized Types

List<Integer>[] arrayOfLists = new List<Integer>[2];  // compile-time error
Object[] strings = new String[2];
strings[0] = "hi";   // OK
strings[1] = 100;    // An ArrayStoreException is thrown.

If you try the same thing with a generic list, there would be a problem:

Object[] stringLists = new List<String>[2];  // compiler error, but pretend it's allowed
stringLists[0] = new ArrayList<String>();   // OK
stringLists[1] = new ArrayList<Integer>();  // An ArrayStoreException should be thrown,
                                            // but the runtime can't detect it.

Cannot Create, Catch, or Throw Objects of Parameterized Types

// Extends Throwable indirectly
class MathException<T> extends Exception { /* ... */ }    // compile-time error

// Extends Throwable directly
class QueueFullException<T> extends Throwable { /* ... */ // compile-time error

public static <T extends Exception, J> void execute(List<J> jobs) {
    try {
        for (J job : jobs)
            // ...
    } catch (T e) {   // compile-time error
        // ...
    }
}

这个是可以的

class Parser<T extends Exception> {
    public void parse(File file) throws T {     // OK
        // ...
    }
}

Cannot Overload a Method Where the Formal Parameter Types of Each Overload Erase to the Same Raw Type

public class Example {
    public void print(Set<String> strSet) { }
    public void print(Set<Integer> intSet) { }
}

A class cannot have two overloaded methods that will have the same signature after type erasure.The overloads would all share the same classfile representation and will generate a compile-time error.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值