泛型的优点

  1. 泛型类型可以提高可靠性和可读性
  2. 泛型可以参数化类型
  3. 泛型可以让我们在编译时而不是在运行时检测出错误

泛型类或方法允许用户指定可以和这些类或方法一起工作的对象类型(相容的对象)
泛型类ArrayList、泛型接口Comparable
从JDK1.5开始,Java允许定义泛型类、泛型接口、泛型方法

泛型实例化

Java进阶之泛型
泛型就是限制对象的数据类型

泛型的正确使用

举个栗子吧

创建一个存储字符串的线性表(ArrayList)

ArrayList<String> list = new ArrayList<>();

现在就只能向该线性表中添加字符串

list.add("a");

如果试图向其中添加非字符串类型,就会产生编译错误

list.add(new Person("张三",23));

当没有使用泛型时,编译通过,运行也通过,即可以添加任何元素,但这样做在遍历元素进行相关操作时会产生隐藏的bug。

 
泛型类型必须是引用数据类型,不能使用基本数据类型。

错误示范

ArrayList<int> list = new ArrayList<>();

正确示范

ArrayList<Integer> list = new ArrayList<>();
list.add(5);      //自动装箱

 
使用泛型对数据类型进行限制后,无须类型转换就可以从一个线性表中获取一个值,因为编译器已经知道了这个元素的类型。

使用泛型前

ArrayList list = new ArrayList();
list.add("Jone");              //自动提升为Object类型
list.add("Smith");
String s = (String)(list.get(0));//将Object类型转为String类型

使用泛型后

ArrayList<Double> list = new ArrayList<>();
list.add(5.5);          //5.5 is automatically converted to new Double(5.5)
list.add(3.0);          //3.0 is automatically converted to new Double(3.0)
Double doubleObject = list.get(0);  //No casting is needing
double d = list.get(1);             //Automatically converted to double

定义泛型类和接口

可以为类或者接口定义泛型。当使用容器类来创建对象,或者使用容器类或接口来声明引用变量时,必须指定具体的类型。

利用ArrayList模拟堆栈结构,使用泛型对元素类型进行限制

public class Stack<E>{
        //构造方法
        public Stack(){}

        //创建数组列表,存储元素的数据类型为E,需导包alt+shift+O
        private ArrayList<E> list = new ArrayList<>();

        //返回栈中的元素数目
        public int getSize(){
                return list.size();
        }

        //返回栈顶元素
        public E peek(){
                return list.get(getSize() - 1);
        }

        //返回并移除栈顶元素
        public E pop(){
                E o = list.get(getSize() - 1);
                list.remove(getSize() - 1);
                return o;
        }

        //添加一个新元素到栈顶
        public void push(E o){
                list.add(o);
        }

        //如果栈为空,就返回true
        public boolean isEmpty(){
                return list.isEmpty();
        }

        @Override
              public String toString(){
                return "stack : " + list.toString();
        }
}

若使用LinkedList模拟堆栈结构,可以用getLast()、removeLast()、addLast()方法
上面自定义类的构造方法不是public Stack<E>(){},而是public Stack(){}

泛型可能会有多个参数,应将所有参数一起放在尖括号中,并用逗号分隔开,比如<E1,E2,E3>
可以定义一个类或接口作为泛型类或者泛型接口的子类型
例如

public final class String
extends Object
implements Serializable, Comparable<String>, CharSequence

泛型方法

可以为静态方法定义泛型类型

public clas GenericMethodDemo{
        public static void main(String[] args){
                Integer[] integers = {1,2,3,4,5};
                String[] strings = {"London","Paris","New York","Austin"};

                GenericMethodDemo.<Integer>print(integers);
                GenericMethodDemo.<String>print(strings);
         }
         public static <E> void print(E[] list){
         for(int i = 0;i<list.length;i++){
                 System.out.print(list[i]+" ");
                 System.out.println();
        }
}

为了声明泛型方法,将泛型类型<E>置于方法声明中关键字static之后

public static <E> void print(E[] list)

为了调用泛型方法,需要将实际类型放在尖括号内作为方法名的前缀。例如,

GenericMethodDemo.<Integer>print(integers);
GenericMethodDemo.<String>print(strings);

简单调用

print(integers);
print(strings);

 

可以将泛型指定为另外一种类型的子类型,这样的泛型类型称为受限的。例如下面所示的代码用以测试两个几何对象是否具有相同的面积。受限的泛型类型将E指定为GeometricObject的泛型子类型。此时必须传递两个GeomericObject的实例来调用equalArea

public class BoundedTypeDemo{
    public static void main(String[] args){
        Rectangle rectangle = new Rectangle(2,2);
        Circle circle = new Circle(2);
        System.out.println("Same area?" + equalArea(rectangle,circle));
    }
    public static <E extends GeometricObject> boolean equalArea(
            E object1,E object2){
        return object1.getArea()==object2.getArea();
    }
}

非受限泛型类型<E>等同于<E extends Object>
为了定义一个类为泛型类型,需要将泛型类型放在类名之后,例如,GenericStack
为了定义一个方法为泛型类型,要将泛型类型放在方法返回类型之前,例如,void max(E o1,E o2)