zoukankan      html  css  js  c++  java
  • Java—泛型详解和使用

    1 泛型介绍

    1.1 泛型的出现

      泛型的出现还得从集合说起,没有泛型的时候,我们将一个对象存入集合时,集合不care这个对象的数据类型是什么,当我们再次从这个对象取出来的时候,对象的编译类型会变成Object类型,这时候我们就需要强制类型转换,但是这种行为每次都要去指定类型进行强制转换,并且有可能强制转换不了,比如我存的是Integer类型,误转换为String类型,那就可能会引发ClassCastException异常。
      当Java 5引入了一个叫做“参数化类型”的概念后,我们可以在创建集合时去指定集合,这样我们再从集合取出数据时,这个数据就是我们当初指定的类型,不会出现需要强制类型转换的情况了。这种参数化类型就是泛型

    1.2 泛型在集合中的使用示例

      在集合接口或者类后面增加尖括号<>,里面注明数据类型,创建这个集合后,这个集合就只能存储这个特定的数据类型对象。
      从Java 7开始,在使用带泛型的接口、类定义变量,我们调用构造器创建对象时构造器后面不需要带完整的泛型信息,只需要带一对尖括号<>就行。如List<String> strList = new ArrayList<>();只需要前面声明<String>泛型,后面只需要<>即可。
    示例:

    public class DemoApplication {
        public static void main(String[] args) {
            List<String> stringList = new ArrayList<>();
            stringList.add("t1");
            stringList.add("t11");
            Map<String, List<String>> map = new HashMap<>();
            map.put("t", stringList);
            stringList.forEach(str -> System.out.println(str.length()));
            map.forEach((key, value) -> System.out.println(key + "---->" + value));
        }
    }
    

    结果:

    2
    3
    t---->[t1, t11]
    

    2 泛型的进阶

      泛型就是允许在定义接口、类和方法时使用类型形参,通过传入实际的类型参数(类型实参),该类型形参会在声明变量、创建对象、调用方法的时候动态指定。类型形参可以在整个接口、类内当作类型使用。这种方式可以动态生成无限个逻辑上的子类(实际物理中没有)。
      无论泛型的类型形参传入的是什么类型实参,系统最后都是当作一个类来处理,内存中也只是占用一块内存空间。如List<Integer> intList = new ArrayList<>();List<String> strList = new ArrayList<>();通过intList.getClass()strList.getClass()得到的是相等的结果,这就表明两个类是相同的。

    另: 在静态变量和静态方法声明中不可以使用类型形参。

    2.1 泛型接口举例

    public interface List<T> {
    	void create(T e);
    	void delete(T e);
    	void update(T e);
    	void find(T e);
    }
    

    其中,List为例,若形参T传入的是String类型的实参,则会产生一个List<String>,逻辑上是List的子接口,只不过这个接口内的E类型都为String类型。

    2.2 泛型类举例

    定义泛型类Person

    //创建带泛型声明的自定义类
    public class Person<T>{
    
        //使用T类型形参来定义实例变量
        private T info;
    
        public Person(){}
    
        //使用T类型形参定义构造器,注意:构造器不用增加泛型声明,直接使用原类名即可;
        public Person(T info) {
            this.info = info;
        }
    
        public T getInfo() {
            return info;
        }
    
        public void setInfo(T info) {
            this.info = info;
        }
    
        @Override
        public String toString() {
            return info.toString();
        }
    }
    

    泛型形参传入实参测试:

    public class DemoApplication {
        public static void main(String[] args) {
        	
        	//实参传入String类型给T形参,则构造器构造对象时传参为String类型;
    		Person<String> personStr = new Person<>("小明");
            System.out.println("姓名: " + personStr.getInfo());
            
        	//实参传入Integer类型给T形参,则构造器构造对象时传参为Integer类型;
            Person<Integer> personInt = new Person<>(28);
            System.out.println("年龄: " + personInt.getInfo());
    	}
    }
    

    结果:

    姓名: 小明
    年龄: 28
    

    从上面的实验中,我们可以看出:通过泛型类Person<T>传入实参后,可以生成诸如Person<String>Person<Integer>Person<Float>等多个逻辑子类。

    2.3 泛型类无参子类

    创建ChinesePerson类继承无参泛型类。

    public class ChinesePerson extends Person {
        @Override
        public String getInfo() {
            return super.getInfo().toString();
        }
    }
    

    测试:

    public class DemoApplication {
        public static void main(String[] args) {
        
            Person<String> chinesePerson = new ChinesePerson();
            chinesePerson.setInfo("中国人");
    
            System.out.println("Chinese person: " + chinesePerson.getInfo());
    	}
    }
    

    结果:

    Chinese person: 中国人
    

    2.5 泛型类带参子类

    创建JiangsuPerson类继承带参泛型类。

    public class JiangsuPerson extends Person<String> {
        //重写父类方法,返回值类型需保持一致
        public String getInfo() {
            return "城市信息: " + super.getInfo();
        }
    }
    

    测试:

    public class DemoApplication {
        public static void main(String[] args) {
    
            Person<String> jiangsuPerson = new JiangsuPerson();
            jiangsuPerson.setInfo("南京");
            System.out.println(jiangsuPerson.getInfo());
    
    	}
    }
    

    结果:

    城市信息: 南京
    

    3 泛型的类型通配符

    3.1 类型通配符<?>

      通配符?在类型形参中使用后,可以表示各种泛型的父类,如List<?>,即List的类型未知,可以传入任何类型的实参。
      这种List<?>不能直接添加元素,因为无法确定集合内的类型是什么,所以无法添加对象。(除了null,因为null是所有引用类型的实例)
      List<?>通过get()方法返回的一定是Object,所以可以将其赋值给Object类型变量。

    3.2 类型通配符上限<? extends xx>

      通过<? extends xx>表示所有xx泛型的父类,如List<? extends Person>表示所有Person泛型List的父类。?代表的是一个未知类型,但这个未知类型又受到extends限制,只能是Person的自身及子类。

    3.3 类型形参的上限

      类型形参的上限主要是限制实参只能是形参类型本身或子类。
    示例:

    public class Person<T extends Number> {
    	T info;
    	public static void main(String[] args) {
    		Person<Integer> person = new Person<>();
    		Person<Float> person2 = new Person<>();
    	}
    }
    

    若上述代码中增加Person<String> person3 = new Person<>();,则编译报错,因为String类型不是Number或子类;通过<T extends xx>限制住形参的上限。

    4 泛型方法

    4.1 泛型方法定义

      泛型方法就是在声明方法时定义一个或多个类型形参;相比较于普通方法,泛型方法多了类型形参声明,多了<>,若有多个类型形参,使用逗号","隔开,并且在方法修饰符和方法返回值类型中间要有类型形参。

    4.2 语法

    修饰符 <T, S> 返回值类型 方法名(形参列表){
    	//方法体
    }
    

    4.3 举例

    泛型方法:

    public class GenericMethodExample {
        //定义泛型方法,传递T类型形参
        public static <T> void copyArrayToCollection(T[] array, Collection<T> collection) {
            for (T obj : array) {
                collection.add(obj);
            }
        }
    
        //定义泛型方法,通过类型通配符设置T类型上限
        public static <T> void copyCollectionToCollection(Collection<? extends T> fromCollection,
                                                          Collection<T> toCollection) {
            for (T obj : fromCollection) {
                toCollection.add(obj);
            }
        }
    }
    

    入口类:

    public class DemoApplication {
        public static void main(String[] args) {
    		//泛型方法
            Object[] objects = new Object[]{"10", 12, new Person("hello, i'm from jiangsu")};
            Collection<Object> collections = new ArrayList<>();
            GenericMethodExample.copyArrayToCollection(objects, collections);
            collections.forEach(obj -> System.out.println(obj.toString()));
            
            //泛型方法通过类型通配符设置上限类型形参
            List<Integer> fromCollection = new ArrayList<>();
            fromCollection.add(1);
            fromCollection.add(2);
            fromCollection.add(3);
            Collections.reverse(fromCollection);
            List<Number> toCollection = new ArrayList<>();
            GenericMethodExample.copyCollectionToCollection(fromCollection, toCollection);
            System.out.println("泛型方法上限测试:" + toCollection);
    	}
    }
    

    运行结果:

    10
    12
    hello, i'm from jiangsu
    泛型方法上限测试:[3, 2, 1]
    

      从上述测试中,我们可以看到<T extends ?这种通配符设置类型形参上限,何时使用通配符,何时使用泛型方法?我们主要通过依赖关系来判断。如果方法中的一个或多个参数之间的类型是有依赖关系的,或者方法的返回值和参数类型有依赖关系,我们就使用泛型方法;反之,没有任何依赖关系的,我们就使用通配符去灵活的使用泛型。

    5 泛型构造器

    5.1 泛型构造器定义

      在构造器签名中声明类型形参就是泛型构造器。

    5.2 语法

    public <T> 类名(形参列表){
    	//方法体
    }
    

    5.3 示例

    给GenericMethodExample类定义泛型构造方法

    public class GenericMethodExample {
        
        public <T> GenericMethodExample(T t) {
            System.out.println(t);
        }
    }
    

    主类入口:

    public class DemoApplication {
        public static void main(String[] args) {
            new GenericMethodExample("泛型构造器01");
            new GenericMethodExample(100);
    
            new <String> GenericMethodExample("显示指定泛型构造器形参T为String类型");
            new <Integer> GenericMethodExample(50);
    	}
    }
    

    运行结果:

    泛型构造器01
    100
    显示指定泛型构造器形参T为String类型
    50
    

    参考《疯狂Java》

  • 相关阅读:
    《编程珠玑》读后感之一
    《梦断代码》读后感之三
    java项目中下载文件文件名乱码
    struts中action与页面之间的传值方式
    使用JSON数据报错和方法
    java中实现将一个数字字符串转换成逗号分隔的数字串, 即从右边开始每三个数字用逗号分隔
    java中判断一个字符在字符串中出现的次数
    使用面向对象(OO)的思想,实现循环输入多个会员的信息,根据会员编号,查找会员积分
    MySQL添加用户、删除用户与授权
    vi编辑器的使用
  • 原文地址:https://www.cnblogs.com/Andya/p/12525562.html
Copyright © 2011-2022 走看看