Java泛型

什么是Java泛型?

所谓泛型,就是变量类型的参数化。

泛型是JDK1.5中一个最重要的特征。通过引入泛型,我们将获得编译时类型的安全和运行时更小的抛出ClassCastException的可能。

在JDK1.5中,你可以声明一个集合将接收/返回的对象的类型。

使用泛型时如果不指明参数类型,即泛型类没有参数化,会提示警告,此时类型为Object。

为什么要使用泛型?

让我们先看一个示例:

package cn.com.example;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by Jack on 2017/1/17.
 */
public class Test {

    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("jack");
        list.add("rose");
        list.add(24324);

        for (int i = 0; i < list.size(); i++) {
            String name = (String) list.get(i);
            System.out.println(name);
        }
    }
}

结果:

Exception in thread "main" jack
rose
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
	at cn.com.example.Test.main(Test.java:18)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)

当我们将一个对象放入集合中,集合不会记住此对象的类型,当再次从集合中取出此对象时,改对象的编译类型变成了Object类型,但其运行时类型任然为其本身类型。

为什么会出现 java.lang.ClassCastException异常?

因为 String name = (String) list.get(i); 需要进行强制类型转换 所以出现了 java.lang.ClassCastException异常。

有什么方法可以解决这个问题? 答案就是泛型

让我们修改下上面的示例:

package cn.com.example;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by Jack on 2017/1/17.
 */
public class Test {

    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        list.add("jack");
        list.add("rose");
        // list.add(24324); // 编译出错

        for (int i = 0; i < list.size(); i++) {
            String name = list.get(i); // 不需要强制转换
            System.out.println(name);
        }
    }
}

运行结果:

jack
rose

采用泛型写法后,List想加入一个Integer类型的对象时会出现编译错误,通过List<String>,直接限定了list集合中只能含有String类型的元素,List get无须进行强制类型转换,因为此时,集合能够记住元素的类型信息,编译器已经能够确认它是String类型了。

下面我们看看List接口的定义:

public interface List<E> extends Collection<E> {

    int size();

    boolean isEmpty();

    boolean contains(Object o);

    Iterator<E> iterator();

    Object[] toArray();

    <T> T[] toArray(T[] a);

    boolean add(E e);

    boolean remove(Object o);

    boolean containsAll(Collection<?> c);

    boolean addAll(Collection<? extends E> c);

    boolean addAll(int index, Collection<? extends E> c);

    boolean removeAll(Collection<?> c);

    boolean retainAll(Collection<?> c);

    void clear();

    boolean equals(Object o);

    int hashCode();

    E get(int index);

    E set(int index, E element);

    void add(int index, E element);

    E remove(int index);

    int indexOf(Object o);

    int lastIndexOf(Object o);

    ListIterator<E> listIterator();

    ListIterator<E> listIterator(int index);

    List<E> subList(int fromIndex, int toIndex);
}

在List接口中采用泛型化定义之后,<E>中的E表示类型形参,可以接收具体的类型实参,并且此接口定义中,凡是出现E的地方均表示相同的接受自外部的类型实参。

我们在来看看List 实现类 ArrayList实现:

public class ArrayList<E> extends AbstractList<E> 
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    
    public E get(int index) {
        rangeCheck(index);
        checkForComodification();
        return ArrayList.this.elementData(offset + index);
    }
}

我们从源代码角度明白了为什么List加入Integer类型对象编译错误,且List get()到的类型直接就是String类型了。

自定义泛型接口、泛型类、泛型方法

自定义泛型接口、泛型类和泛型方法与上述Java源码中的List、ArrayList类似。

示例:

package cn.com.example;

/**
 * Created by Jack on 2017/1/17.
 */
public class Test {

    public static void main(String[] args) {
        Example<String> example = new Example<String>("Jack");
        System.out.println("value:" + example.getValue());
    }
}

class Example<T> {

    T value;

    public Example() {

    }

    public Example(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }
}

运行结果:

value:Jack

类型通配符

我们先来看一个示例:

package cn.com.example;

/**
 * Created by Jack on 2017/1/17.
 */
public class Test {

    public static void main(String[] args) {

        Example<Number> name = new Example<Number>(99);
        Example<Integer> age = new Example<Integer>(712);

        getValue(name);

        // getValue(age); // 编译出错
    }

    public static void getValue(Example<Number> example){
        System.out.println("value :" + example.getValue());
    }
}

class Example<T> {

    T value;

    public Example() {

    }

    public Example(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }
}

getValue(age); // 编译出错 那怎么解决这个问题呢? 答案就是使用通配符

让我们修改上面的代码

package cn.com.example;

/**
 * Created by Jack on 2017/1/17.
 */
public class Test {

    public static void main(String[] args) {

        Example<Number> name = new Example<Number>(99);
        Example<Integer> age = new Example<Integer>(712);

        getValue(name);

        getValue(age);
    }

    public static void getValue(Example<? extends Number> example){
        System.out.println("value :" + example.getValue());
    }
}

class Example<T> {

    T value;

    public Example() {

    }

    public Example(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }
}

运行结果:

value :99
value :712

Example<? extends Number> example 这样就可以编译通过了 意思就是Number的子类都可以使用。

补充:

泛型中 T K V E 各代表的意思

T代表java类型

K V 代表java键值中的key和value

E代表Element

?代表不确定的java类型

原文地址:https://www.cnblogs.com/Zombie-Xian/p/6377317.html