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 }

 

posted on 2017-01-14 21:32  kuillldan  阅读(163)  评论(0编辑  收藏  举报