• Java中的泛型


    泛型技术在绝大多数语言总都存在。对泛型狭义的解释就是,在定义类的时候不指定属性或方法参数的类型,用一个T或者其他单词代替类型,等到实例化对象的时候根据实际需要指定类型。这样可以极大的提高代码的重用性和健壮性。如下面的代码:

     1 class Message<T>
     2 {
     3     private T info;
     4     public Message(T info)
     5     {
     6         this.info = info;
     7     }
     8     
     9     public T getInfo()
    10     {
    11         return info;
    12     }
    13     
    14     public void setInfo(T info)
    15     {
    16         this.info = info;
    17     }
    18     
    19     public void show()
    20     {
    21         //利用反射获取info变量的类型
    22         System.out.println("类型:" + this.info.getClass().getSimpleName());
    23          
    24         System.out.println("值:" + this.info);
    25     }
    26 }

    上面的代码在类声明的时候并没有指定属性info的具体类型,而是用字母T代替。下面是测试代码:

     1 public class Main
     2 {
     3     public static void main(String[] args)
     4     { 
     5         Message<String> msg1 = new Message<String>("hello");
     6         msg1.show();
     7         System.out.println("----------");
     8         Message<Integer> msg2 = new Message<Integer>(10086);
     9         msg2.show();
    10         System.out.println("Main Done//~~");
    11     }
    12 }

    可以看到,我们的测试代码在实际运行的时候才指定了具体类型。下面是输出结果:

    类型:String
    值:hello
    ----------
    类型:Integer
    值:10086
    Main Done//~~

    泛型的通配符

    先看下面的代码:

     1 public class Main
     2 {
     3     public static void main(String[] args)
     4     { 
     5         Message<String> msg = new Message<String>("hello");
     6         printInfo(msg);
     7         
     8         System.out.println("Main Done//~~");
     9     }
    10     
    11     public static void printInfo(Message<String> msg)
    12     {
    13         msg.show();
    14     }
    15 }

    我们可以将msg变量传入到函数中,这完全没有问题。但是,如果此时的msg用下面的代码声明,结果会如何呢?

     1 public class Main
     2 {
     3     public static void main(String[] args)
     4     { 
     5         Message<Integer> msg = new Message<Integer>(10086);
     6         printInfo(msg);
     7         
     8         System.out.println("Main Done//~~");
     9     }
    10     
    11     public static void printInfo(Message<String> msg)
    12     {
    13         msg.show();
    14     }
    15 }

    这段代码无法通过编译。因为函数是Message<String>作为形参类型,而主函数中传入的是Message<Integer>类型。为了解决这个问题,我们可以修改函数的形参类型,去掉后面的泛型,直接用Message接收参数:

     1 public class Main
     2 {
     3     public static void main(String[] args)
     4     { 
     5         Message<Integer> msg = new Message<Integer>(10086);
     6         printInfo(msg);
     7         
     8         System.out.println("Main Done//~~");
     9     }
    10     
    11     public static void printInfo(Message msg)
    12     {
    13         msg.show();
    14     }
    15 }

    但是,这并不是解决该问题的最佳方案。因为一旦我们取消掉泛型限制,实际上info类型变成了Object类型。这样会导致一个问题就是,我们在pritnInfo函数中,可以传递一个String类型给info,这就严重违背了我们在主方法中将msg变量设置为Message<String>类型的初衷。请看下面的代码:

     1 public class Main
     2 {
     3     public static void main(String[] args)
     4     { 
     5         Message<Integer> msg = new Message<Integer>(10086);
     6         printInfo(msg);
     7         
     8         System.out.println("Main Done//~~");
     9     }
    10     
    11     public static void printInfo(Message msg)
    12     {
    13         msg.setInfo("hello");
    14         msg.show();
    15     }
    16 }

    输出结果:

    类型:String
    值:hello
    Main Done//~~

    为了解决这个问题,我们可以采用通配符<?>来作为函数的形参类型。

    public class Main
    {
        public static void main(String[] args)
        { 
            Message<Integer> msg = new Message<Integer>(10086);
            printInfo(msg);
            
            System.out.println("Main Done//~~");
        }
        
        public static void printInfo(Message<?> msg)
        {
            msg.show();
        }
    }

    但是,一旦设置了<?>接收参数,在printInfo中就只能读取参数值,不能再设置参数值。下面的代码不能通过编译:

     1 public class Main
     2 {
     3     public static void main(String[] args)
     4     { 
     5         Message<Integer> msg = new Message<Integer>(10086);
     6         printInfo(msg);
     7         
     8         System.out.println("Main Done//~~");
     9     }
    10     
    11     public static void printInfo(Message<?> msg)
    12     {
    13         msg.setInfo(10010);
    14         msg.show();
    15     }
    16 }

    泛型上限和下限

    在通配符的基础上还有下面两个子通配符:

      <? extends Type> 表示通配的类型必须是类型Type或者Type的子类

      <? super Type> 表示通配符必须是类型Type或者Type的父类

    如下面的代码,只能是Number类型或者Number类型的子类才能传递给printInfo函数:

     1 public class Main
     2 {
     3     public static void main(String[] args)
     4     { 
     5         Message<Integer> msg = new Message<Integer>(10086);
     6         printInfo(msg);
     7         
     8         System.out.println("Main Done//~~");
     9     }
    10     
    11     public static void printInfo(Message<? extends Number> msg)
    12     { 
    13         msg.show();
    14     }
    15 }

    下面的代码不能通过编译:

     1 public class Main
     2 {
     3     public static void main(String[] args)
     4     { 
     5         Message<String> msg = new Message<String>("hello");
     6         printInfo(msg);
     7         
     8         System.out.println("Main Done//~~");
     9     }
    10     
    11     public static void printInfo(Message<? extends Number> msg)
    12     { 
    13         msg.show();
    14     }
    15 }

    有如下面的代码,只能向printInfo传递String以及String的父类才行:

    public class Main
    {
        public static void main(String[] args)
        { 
            Message<String> msg = new Message<String>("hello");
            printInfo(msg);
            
            System.out.println("Main Done//~~");
        }
        
        public static void printInfo(Message<? super String> msg)
        { 
            msg.show();
        }
    }

    下面的代码不能通过编译,因为Integer不是String的父类:

     1 public class Main
     2 {
     3     public static void main(String[] args)
     4     { 
     5         Message<Integer> msg = new Message<Integer>(10086);
     6         printInfo(msg);
     7         
     8         System.out.println("Main Done//~~");
     9     }
    10     
    11     public static void printInfo(Message<? super String> msg)
    12     { 
    13         msg.show();
    14     }
    15 }
  • 相关阅读:
    java的运行机制及初步相关配置(jdk)
    观察者模式
    Shiro的 rememberMe 功能使用指导(为什么rememberMe设置了没作用?)
    MyBatis—实现关联表查询
    Mybatis解决字段名与实体类属性名不相同的冲突
    Mybatis简化sql书写,别名的使用
    十八.模块
    十七.偏函数
    十六.装饰器
    十五.匿名函数
  • 原文地址:https://www.cnblogs.com/kuillldan/p/6286149.html
Copyright © 2020-2023  润新知