Java泛型使用示例整理

作者: 信海龙 | 来源:发表于2016-12-02 22:40 被阅读1356次

    Java泛型使用示例整理

    目标

    Java泛型编程是JDK1.5版本后引入的。泛型让编程人员能够使用类型抽象,通常用于集合里面。本文旨在整理一些泛型的用法。

    用法

    泛型分两部分。一部分是泛型类和方法的定义。另一部分是泛型类和方法的使用。

    定义篇

    类定义时,使用泛型

    在定义类的时候,我们可以使用泛型。如下代码:

    class Demo <T> {
        T field;
        
        public void setFiled(T field) {
            this.field = field;
        }
        
        public T getField() {
            return field;
        }
    }
    
    public class Test {
        
        public static void main (String[] args) throws java.lang.Exception
        {
            Demo<String> demo = new Demo<String>();
            demo.setFiled("www.bo56.com");
            System.out.println(demo.getField());
        }
    }
    

    类名后面增加<T>,说明是泛型类。T可以视为类型的占位符。泛型类的代码就可以使用这个占位符T。

    无参数方法定义时,使用泛型

    无论在泛型类,还是普通类中,我们都可以再方法中使用泛型。

    import java.util.ArrayList;
    import java.util.List;
    
    class Demo {
        
        public <T> List<T> newArrayList() {
            return new ArrayList<T>();
        }
    }
    
    public class Test {
        
        public static void main (String[] args) throws java.lang.Exception
        {
            Demo demo = new Demo();
            List<String> list = demo.newArrayList();
            list.add("www.bo56.com");
            list.add("bo56.com");
            //list.add(1); 报错。只能添加String
            for (String str:list) {
                System.out.println(str);
            }
        }
    }
    

    方法的返回值前面,修饰符后面增加<T>,表示为泛型方法。这样,就可以在方法的代码中使用T代表类型。
    没有参数的泛型方法,类型的确定,是根据等号左边的类型推导泛型的最终类型。

    有参数方法定义时,使用泛型

    class Demo {
        
        public <T> void showClass(T t) {
            System.out.println(t.getClass());
        }
    }
    
    public class Test {
        
        public static void main (String[] args) throws java.lang.Exception
        {
            Demo demo = new Demo();
            demo.showClass("123");
            demo.showClass(123);
        }
    }
    

    有参数的泛型方法,类型的确定,是根据参数类型自动推导。

    方法定义时,使用通配符 ?

    import java.util.ArrayList;
    import java.util.List;
    
    class Demo {
        
        public void show(List<?> list) {
            list.add(null);
            //list.add(123); 编译错误
            for (Object object:list) {
                System.out.println(object);
            }
        }
    }
    
    public class Test {
        
        public static void main (String[] args) throws java.lang.Exception
        {
            Demo demo = new Demo();
            List<String> listStr = new ArrayList<String>();
            listStr.add("abc");
            demo.show(listStr);
            List<Long> listLong = new ArrayList<Long>();
            listLong.add(123L);
            demo.show(listLong);
        }
    }
    

    1、只能往集合中add null。
    2、因为集合中的类型不确定。因此,为了安全,转换为Object。

    类或者方法定义时,使用通配符 <T extends Number>

    class Demo {
        
        public <T extends Number> void showClass(T t) {
            System.out.println(t.getClass());
        }
    }
    
    public class Test {
        
        public static void main (String[] args) throws java.lang.Exception
        {
            Demo demo = new Demo();
            demo.showClass(123);
            demo.showClass(123f);
            demo.showClass(123L);
            // demo.showClass("123"); 有错误 参数的类型,只能是 Number类型或者其子类
        }
    }
    

    <T extends Number> 表示传入的类型必须是Number或者其子类型。

    使用篇

    指定固定的类型

    import java.util.ArrayList;
    import java.util.List;
    
    public class Test {
        
        public static void main (String[] args) throws java.lang.Exception
        {
            List<Long> list = new ArrayList<Long>();
            //List<Number> listN = new ArrayList<Long>(); 编译错误。List<Number> 并不是 ArrayList<Long> 的父类。
        }
    }
    

    如果变量声明时,为泛型指定的类型为固定类型。如List<Long>,就是为泛型指定的类型为Long。那么后续在给变量赋值时,指定的变量也得为这个类型。如 ArrayList<Long>(),指定的也是Long。
    对于泛型来说,Long是Number的子类。但是,List<Long>并不是List<Number>的子类。

    使用通配符 ?

    import java.util.ArrayList;
    import java.util.List;
    
    class Food {}
    
    class Fruit extends Food {}
    
    class Apple extends Fruit {}
    
    public class Test {
        
        public static void main (String[] args) throws java.lang.Exception
        {
            List<?> list = new ArrayList<Fruit>();
            //list.add(new Food()); 编译错误
            //list.add(new Fruit()); 编译错误
            //list.add(new Apple()); 编译错误
            list.add(null);
            //Food food = list.get(0);
            //Fruit fruit = list.get(0);
            //Apple apple = list.get(0);
            Object object = list.get(0);
        }
    }
    

    1、只能添加null。
    2、获取的值只能赋值给Object类型。
    因为通配符?表示该集合存储的元素类型未知,可以是任何类型。往集合中加入元素需要是一个未知元素类型的子类型,正因为该集合存储的元素类型未知,所以我们没法向该集合中添加任何元素。唯一的例外是null,因为null是所有类型的子类型,所以尽管元素类型不知道,但是null一定是它的子类型。

    使用上界通配符 <? extends Fruit>

    import java.util.ArrayList;
    import java.util.List;
    
    class Food {}
    
    class Fruit extends Food {}
    
    class Apple extends Fruit {}
    
    public class Test {
        
        public static void main (String[] args) throws java.lang.Exception
        {
            List<? extends Fruit> list = new ArrayList<Fruit>();
            // List<? extends Fruit> listA = new ArrayList<Food>(); 编译错误。不能为父类。
            List<? extends Fruit> listN = new ArrayList<Apple>();
            listN.add(null);
            //listN.add(123); 不能add
            Fruit fruit = listN.get(0);
            Food food = listN.get(0);
            //Apple apple = listN.get(0); 编译错误。get获取的值,只能给父类
            listN.remove(0);
        }
    }
    

    上界通配符,一般用于读取的场景。
    1、为泛型指定的类型只能是Fruit类型或者其子类。
    2、只能为其列表添加null。
    3、get方法获取的值只能赋值给Fruit类或者其超类。

    使用下界通配符 <? super Fruit>

    import java.util.ArrayList;
    import java.util.List;
    
    class Food {}
    
    class Fruit extends Food {}
    
    class Apple extends Fruit {}
    
    public class Test {
        
        public static void main (String[] args) throws java.lang.Exception
        {
            List<? super Fruit> list = new ArrayList<Fruit>();
            List<? super Fruit> listA = new ArrayList<Food>(); 
            //List<? super Fruit> listN = new ArrayList<Apple>(); 编译错误,不能为子类
            listA.add(new Fruit());
            //listA.add(new Food()); 编译错误,不能为父类。
            listA.add(new Apple());
            Object object = listA.get(0);
            //Fruit fruit = listA.get(0);编译错误。
            //Food food = listA.get(0);编译错误。
            //Apple apple = listA.get(0); 编译错误。
        }
    }
    

    下界通配符,一般用于写入的场景。
    1、为泛型指定的类型必须为Fruit,或者其超类。
    2、可以为其列表添加任意Fruit类型,或者其子类。
    3、get方法获取的类型,只能赋值给Object类型。

    边界通配符总结

    边界通配符总结
    如果你想从一个数据类型里获取数据,使用 ? extends 通配符
    如果你想把对象写入一个数据结构里,使用 ? super 通配符
    如果你既想存,又想取,那就别用通配符。

    注意

    泛型类型是被所有调用共享的

    import java.util.ArrayList;
    import java.util.List;
    
    public class Test {
        
        public static void main (String[] args) throws java.lang.Exception
        {
            List<String>l1 = new ArrayList<String>();  
            List<Integer>l2 = new ArrayList<Integer>();  
            System.out.println(l1.getClass() == l2.getClass()); //True  
        }
    }
    

    所有泛型类的实例都共享同一个运行时类,类型参数信息会在编译时被擦除。因此考虑如下代码,虽然ArrayList<String>和ArrayList<Integer>类型参数不同,但是他们都共享ArrayList类,所以结果会是true。

    instanceof

    import java.util.ArrayList;
    import java.util.Collection;
    
    public class Test {
        
        public static void main (String[] args) throws java.lang.Exception
        {
            Collection cs = new ArrayList<String>();  
            if (cs instanceof Collection<String>){}// compile error.如果改成instanceof Collection<?>则不会出错。  
        }
    }
    

    不能对确切的泛型类型使用instanceOf操作。如下面的操作是非法的,编译时会出错。

    泛型数组问题

    import java.util.ArrayList;
    import java.util.List;
    
    public class Test {
        
        public static void main (String[] args) throws java.lang.Exception
        {
            List<String>[] lsa = new ArrayList<String>[10]; //compile error.
        }
    }
    

    不能创建一个确切泛型类型的数组。

    
    import java.util.ArrayList;
    import java.util.List;
    
    public class Test {
        
        public static void main (String[] args) throws java.lang.Exception
        {
            List<?>[] lsa = new ArrayList<?>[10]; // ok, array of unbounded wildcard type  
        }
    }
    

    能创建带通配符的泛型数组.

    参考

    http://qiemengdao.iteye.com/blog/1525624
    http://www.cnblogs.com/iyangyuan/archive/2013/04/09/3011274.html

    相关文章

      网友评论

        本文标题:Java泛型使用示例整理

        本文链接:https://www.haomeiwen.com/subject/amiqmttx.html