zoukankan      html  css  js  c++  java
  • 菜鸡的Java笔记 第三十三

    泛型 GenericParadigm
            1.泛型的产生动机
            2.泛型的使用以及通配符
            3.泛型方法的使用
            
            JDK1.5 后的三大主要新特性:泛型,枚举,Annotation
            
            泛型的产生背景

        在 Java SE 5.0 以前操作集合有许多缺点:
          一是从集合取出对象时,需要执行类型转换操作,我们在前面讲过,集合中存储都是以 Object 对象进行存储的,这无疑让我们的操作变得麻烦。
          二是由于没有类型检查,可以向集合添加任意对象,不便于我们对集合的管理,有时候甚至会导致严重的错误。

        泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。
        那么参数化类型怎么理解呢?
          顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。

      

                假如说现在要求你定义一个表示坐标的类:Point ,在这个类中存放有两个属性:x 坐标和 y 坐标
                但是由于此类设计特殊,现在在实际使用中可能出现有以下三种结构的数据:
                    整数:x = 10, y = 10;
                    小数:x = 10.0,y = 20.1;
                    字符串: x = 东经 100 度,y = 北纬 30 度
                现在发现在 Point 类里面可以保存三种数据类型,而 Point 类中应该只会存在有两个属性:x 和 y
                很明显现在唯一可以想到的一定是 Object 类型,因为满足于如下的转换:
                    保存 int:int > 自动装箱为 Integer > 向上转型为 Object
                    保存 double:double > 自动装箱为 Double > 向上转型为 Object
                    保存 String:String > 向上转型为 Object

      
                    
                范例:定义 Point 类

        class Point{
            private Object x;
            private Object y;
            public void setx(){
                this.x = x;
            }
            public void sety(){
                this.y = y;
            }
            public void getx(){
                return x;
            }
            public void gety(){
                return y;
            }
        }

               
                于是下面通过一些重复的操作,分别保存好三个内容
            范例:坐标为整数

        // 第一步:设置坐标数据
        Point p = new Point();
        p.setx(10);
        p.sety(20);
        // 第二步:取得坐标数据
        int x = (Integer) p.getx();
        int y = (Integer)p.gety();
        System.out.println("x= " + x + "y = " + y);

       
            范例:坐标为小数

        // 第一步:设置坐标数据
        Point p = new Point();
        p.setx(10.1);
        p.sety(20.0);
        // 第二步:取得坐标数据
        double x = (Double) p.getx();
        double y = (Double)p.gety();
        System.out.println("x= " + x + "y = " + y);

               
            范例:坐标为字符串

        // 第一步:设置坐标数据
        Point p = new Point();
        p.setx("东经 100 度 ");
        p.sety("北纬 30 度 ");
        // 第二步:取得坐标数据
        String x = (String) p.getx();
        String y = (String)p.gety();
        System.out.println("x= " + x +" "+ "y = " + y);

               
            以上的操作的确是满足了开发要求。但是最严重的问题也就同时产生了,整个代码的实现关键在于利用了 Object 类型
            利用 Object 操作的有点在于可以接收所有的数据类型,但是缺点也就是因为优点造成的: Object 如果要接收数据那么必须进行强制性的向下转型
            
            范例:可能带来的隐患

        // 第一步:设置坐标数据
        Point p = new Point();
        p.setx(100);
        p.sety("北纬 30 度 ");
        // 第二步:取得坐标数据
        String x = (String) p.getx();
        String y = (String)p.gety();
        System.out.println("x= " + x +" "+ "y = " + y);

               
            以上的代码本质上讲一定会存在有问题的,但是现在的程序中明显无法发现这些问题
            因为 Object 本身就可以存放 Integer ,但是这个代码是在项目执行的时候出错
                
                Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
                    at cn.mysterious.GenericParadigm.main(GenericParadigm.java:29)
                    
            所以现在通过分析就可以发现。向下转型这样的操作本身就会存在有安全隐患,而且这种隐患是不能够在编写的时候检查出来的
            传统利用 Object 类来进行处理的操作永远都会有这样的问题
            那么现在既然已经出现了这样的问题,用什么样的方式可以解决此类问题呢?
            唯一的方案,不进行对象的向下转型,但是不转型有又打破了好不容易建立起来的一些优势。所以在这样的背景下产生了泛型技术
            泛型的本质:类中的属性或者方法的参数,在类定义的时候不设置具体的类型,只使用一个标记表示,而在这些类使用的时候才会为其动态的绑定一种数据类型
            
            范例:使用泛型

        class Point<T>{ // T: Type(类型),Param(参数),R:Return
        private T x;
        private T y;
        public void setx(T x){
            this.x = x;
        }
        public void sety(T y){
            this.y = y;
        }
        public T getx(){
            return x;
        }
        public T gety(){
            return y;
        }
    }

           
            实际上这里面就告诉使用者,此时的 Poit 类中的属性类型无法确定,必须在类实例化对象的时候动态的绑定
            
            范例:使用泛型实例化类对象

        // 第一步:设置坐标数据
        Point<String> p = new Point();
        p.setx("东经 100 度");
        p.sety("北纬 30 度 ");
        // 第二步:取得坐标数据
        String x = p.getx();
        String y = p.gety();
        System.out.println("x= " + x +" "+ "y = " + y);

               
                此时没有出现所谓的向上转型或者向下转型,并且操作的类型都是动态分配的
                
                如果在应用了泛型技术的类使用的时候没有设置泛型类型,JDK 默认也不会认为它出现了语法错误,也能够正常的执行
                同时使用 Object 作为默认类型
                
                如果泛型保存的死基本数据类型,则必须使用包装类处理
                
        通配符:“?”
            现在有如下泛型类

        class Message<T>{
            private T info;
            public void setInfo(T info) {
                this.info = info;
            }
            public T getInfo() {
                return info;
            }
        }


                首先类的对象一定是可以发生引用传递操作的,所以以上的代码可以进行对象的引用传递
                
            范例:编写代码

        public class GenericParadigm {
    
            public static void main(String[] args) {
                Message<String> msg = new Message<String>();
                fun(msg);
                System.out.println(msg.getInfo());
            }
            public static void fun(Message<String> temp){
                temp.setInfo("hello");
            }
        }

               
                但是泛型类型可以改变,所以现在将以上使用的泛型类型由 String 变为 Integer ,
                这个时候 fun() 方法将不能正常的使用,并且由于重载是受到类型而不是泛型类型的限制,那么也无法通过重载解决此问题
                可是这样的问题怎么解决呢?那么现在就必须有一种类型可以接收全部的泛型种类,有人说:方法里面不用泛型声明了
                
            范例:不用泛型,则用 Object 描述

        public class GenericParadigm {
    
            public static void main(String[] args) {
                Message<String> msg = new Message<String>();
                fun(msg);
                System.out.println(msg.getInfo());
            }
            public static void fun(Message temp){
                temp.setInfo("hello");
            }
        }

               
            这个时候需要有一种标记,这种标记要满足如下要求
                可以用于泛型上,这样可以避免安全警告
                这个标记使用之后,允许接收任何内容,但是不能修改里面的数据
            为此在泛型中提供有一个重要的通配符“?”
            
            范例:使用通配符描述

        public class GenericParadigm {
    
            public static void main(String[] args) {
                Message<String> msg = new Message<String>();
                msg.setInfo("......");
                fun(msg);
                
            }
            public static void fun(Message<?> temp){
                System.out.println(temp.getInfo());
            }
        }

               
            但是在此时通配符的基础上又扩展了两个子通配符组合
                ? extends 类:设置泛型的上限,此设置可以在类或者方法参数中
                    “? extends Number ”:表示可以使用的泛型只能够是 Nunber 或者是 Number 的子类
                ? super 类:设置泛型的下限 ,可以设置在方法参数中
                    “? super String ”:表示只能够设置 String 或者是其父类 Object
                    
            范例:设置泛型的上限

    package cn.mysterious;
    
    class Message<T extends Number >{
        private T info;
        public void setInfo(T info) {
            this.info = info;
        }
        public T getInfo() {
            return info;
        }
    }
    
    public class GenericParadigm {
    
        public static void main(String[] args) {
            Message<Integer> msg = new Message<Integer>();
            msg.setInfo(1010);
            fun(msg);
            
        }
        public static void fun(Message<? extends Number> temp){
            System.out.println(temp.getInfo());
        }
    }

               
            范例:设置泛型的下限

    package cn.mysterious;
    
    class Message<T >{
        private T info;
        public void setInfo(T info) {
            this.info = info;
        }
        public T getInfo() {
            return info;
        }
    }
    
    public class GenericParadigm {
    
        public static void main(String[] args) {
            Message<String> msg = new Message<String>();
            msg.setInfo("1111");
            fun(msg);
            
        }
        public static void fun(Message<? super String> temp){
            System.out.println(temp.getInfo());
        }
    }


                以后如果在看文档的时候会出现许多这样的标记,要求能看懂
                
        泛型接口(重点)
            在接口上使用泛型就是泛型接口

                 // 接口:Ixxx,抽象类:Abstractxxx,普通类:直接写
                interface IMessage<T >{
                    public void print(T t); // 此方法上使用了泛型
                }

            此时实现了泛型接口,但是对于泛型接口的子类有两种实现模式
                一:在子类继续使用泛型声明        

        package cn.mysterious;
         // 接口:Ixxx,抽象类:Abstractxxx,普通类:直接写
        interface IMessage<T >{
            public void print(T t); // 此方法上使用了泛型
        }
        class Message<P> implements IMessage<P>{
            public void print(P t) {
                // TODO Auto-generated method stub
                System.out.println(t);
            }
        }
        public class GenericParadigm {
    
            public static void main(String[] args) {
                IMessage<String> msg = new Message<String>();
                msg.print("ssssss");
            }
        }

               
                二:在子类定义时不使用泛型,直接为父类接口设置好泛型类型        

        package cn.mysterious;
         // 接口:Ixxx,抽象类:Abstractxxx,普通类:直接写
        interface IMessage<T >{
            public void print(T t); // 此方法上使用了泛型
        }
        class MessageImpl implements IMessage<String>{
            public void print(String t) {
                // TODO Auto-generated method stub
                System.out.println(t);
            }
        }
        public class GenericParadigm {
    
            public static void main(String[] args) {
                IMessage<String> msg = new MessageImpl();
                msg.print("ssssss");
            }
        }

               
                后续学习的时候一定会出现泛型接口的使用,要清楚它的两种实现模式
                
        泛型方法
            如果在一个方法上使用了泛型,那么这个方法就称为泛型方法
            泛型方法不一定非要定义在泛型声明的类中,也可能就是一个方法定义为泛型方法
            
            范例:泛型方法

        package cn.mysterious;
    
        public class GenericParadigm {
    
            public static void main(String[] args) {
                Integer num[] = fun(1,2,3); // 泛型操作
                for (Integer integer : num) {
                    System.out.println(integer);
                }
            }
            public static <T> T[] fun(T ...arg ){ // 声明并使用泛型
                return arg;
            }
        }

               
                从现实来讲泛型方法能看懂就行了
                
        总结
            1.泛型解决的问题:对象向下转型所带来的安全隐患
            2.本质思想:类的属性或方法的参数都是可以由用户在使用的动态设置好的
            3.通配符:?,? extends 类,? super 类
           

  • 相关阅读:
    算法提高---概率计算
    全排列
    算法提高 最小方差生成树
    【洛谷】P1040 加分二叉树
    SPAF模板
    Bellman-Ford算法(有向图)
    Floyd算法
    Dijkstra算法
    蓝桥杯算法提高 递推求值 【矩阵快速幂】
    【动态规划】数字分组I
  • 原文地址:https://www.cnblogs.com/mysterious-killer/p/10123581.html
Copyright © 2011-2022 走看看