zoukankan      html  css  js  c++  java
  • 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类型

  • 相关阅读:
    myEclipse Debug
    C# DataTable的詳細使用方法
    算法 《秦九韶算法java实践》
    【闲聊产品】之五:谁来背黑锅?
    ubuntu install mysql server method
    H264解码的一个測试程序
    Struts2自己定义拦截器实例—登陆权限验证
    【剑指offer】二叉树的镜像
    ubuntu12.04下搭建ftpserver
    C++Vector使用方法
  • 原文地址:https://www.cnblogs.com/Zombie-Xian/p/6377317.html
Copyright © 2011-2022 走看看