javaSE泛型——通配符、泛型接口、类型擦出

一、通配符
1.通配符的概念
前面我们学习的泛型都已经解决了很多问题了,但是我们在使用的时候还是指定了类型的。比如泛型类,我们在创建这个类的对象的时候就指定了是什么类型,然后就只能创建这个类型的对象,那么我们有时候想要创建任意类型的对象的时候我们可以使用通配符。
1.1无解通配符"?"

class Message<T> {
    private T messgae;

    public T getMessage() {
        return messgae;
    }

    public void setMessage(T messgae) {
        this.messgae = messgae;
    }
}
public class TestTongpeifu {
    //1.通配符的使用
    public static void main(String[] args) {
        Message<Integer> message=new Message();//在这可以接收整型的数据
        message.setMessage(55);
        Message<String> message1=new Message<>();//也可以接收字符串
        message1.setMessage("你好");
        Message<Double> message2=new Message<>();//也可以接收双精度的小数
        message2.setMessage(12.35);
        fun(message);
        fun(message1);
        fun(message2);
    }
    public static void fun(Message<?> temp){//这个地方就使用了通配符
        //但是由于通配符不是具体的类型,所以我们不能修改值-> temp.setMessage(100);//error
        System.out.println(temp.getMessage());
        //当我们使用了通配符以后就可以接收任意类型的值了,可以体统给用户的方法是它们可以任意的修改
    }
}

在这里插入图片描述
从上面的例子我们可以看出来,使用通配符以后,我们的同一个方法可以接收不同类型的数据,这样的话我们的泛型显得就更加的有用了。但是一定需要注意的是:在使用通配符的时候我们的数据是不可以更改的,因为在通配符中不知道具体的类型是什么,所以不可以修改。
2.受限泛型
我们由通配符又有了两个子通配符:
extends类:设置泛型上限
super类:设置泛型下限
2.1泛型上限
(1)设置类型通配符的上限

class Generics<T extends Number>{
    private T obj;

    public T getObj() {
        return obj;
    }

    public void setObj(T obj) {
        this.obj = obj;
    }
    public void show(Generics<?> T){
        System.out.println("show():"+getObj());
    }
}
public class TestGenerics {
    public static void main(String[] args) {
        Generics<Integer> generics1=new Generics<>();//创建Generics的实例化对象
        generics1.setObj(20);
        Generics<Double> generics2=new Generics<>();//实例化对象
        generics2.setObj(12.05);
        generics1.show(generics1);
        info(generics1);
        generics2.show(generics2);
        info(generics2);
    }
    public static void info(Generics<? extends Number> t){//这个通配符就是上限的通配符,它所能接收的类型只能是Number的子类
        //这个方法的作用是:接收Generics的任意对象并将其内容打印
        System.out.println("info():"+t.getObj());
    }
}

在这里插入图片描述
2.2泛型下限

//泛型下限
class Message<T>{
    private T message;

    public T getMessage() {
        return message;
    }

    public void setMessage(T message) {
        this.message = message;
    }
}
public class TestGenerics{
    public static void main(String[] args) {
        Message<String> message=new Message<>();
        message.setMessage("nihao");
        fun(message);
    }
    public static void fun(Message<? super String> temp){
        //泛型下限是可以修改的
        temp.setMessage("bit");//修改了我们开始的值
        System.out.println(temp.getMessage());
    }
}

在这里插入图片描述
通过上卖弄我们可以看到,泛型下限是可以修改的,我们本来设置的是"nihao",但是通过泛型下限的方法将这个值修改了;此外还需要注意,经过泛型下限修饰的方法里面的类型只能是这个类型以及这个类型的父类。
二、泛型接口
泛型出了可以定义在类中,也可以定义在接口中。我们称这种该接口为泛型接口。
1.定义泛型接口的格式:
interface IMessage{
public void print(T t);
}
下面是对泛型接口的实现,一共有三种方法:
(1)匿名内部类实现
(2)在子类定义时继续使用泛型
(3)在子类实现接口的时候明确给出具体类型
具体实现如下:

public interface IMessage<T>{
    void print(T t);
}
public class TestInterface {
    public static void main(String[] args) {
//    (1)匿名内部类实现:在创建接口的对象的时候我们将指定具体的类型
        IMessage<String> iMessage =new IMessage<String>() {
            @Override
            public void print(String s) {
                System.out.println(s);
            }
        };

    }
}
//    (2)在子类定义时继续使用泛型
class IMessage1<T> implements IMessage{

    @Override
    public void print(T t) {
        System.out.println(t);
    }
}

//    (3)在子类实现接口的时候明确给出具体类型
class IMessage2 implements IMessage<String>{

    @Override
    public void print(String s) {
        System.out.println(s);
    }
}

三、类型擦出
泛型只存在于代码编译阶段,在进入JVM之前,与泛型相关的信息都会被擦出掉,即是类型擦出。也就是说,泛型类和普通类对于JVm而言是一样的。

class Myclass<T>{                                                   
    private T message;                                              
                                                                    
    public T getMessage() {                                         
        return message;                                             
    }                                                               
                                                                    
    public void setMessage(T message) {                             
        this.message = message;                                     
    }                                                               
    public void testMethod(T t){                                    
        System.out.println(t);                                      
    }                                                               
}                                                                   
public class Cachu {                                                
    public static void main(String[] args) {                        
        Myclass<String> m1=new Myclass<>();                         
        Myclass<Integer> m2=new Myclass<>();                        
        System.out.println(m1.getClass()==m1.getClass());           
    }                                                               
}                                                                   
                                                                    

在这里插入图片描述
上面的例子我们看到的是最后打印的结果是true,说明我们MyClass和MyClass在JVLM中的Class都是MyClass.class。
1.验证泛型类型在运行时被擦出了的两种方法
(1)instanceof
(2)getClass
具体如下:

class Myclass<T>{                                                   
    private T message;                                              
                                                                    
    public T getMessage() {                                         
        return message;                                             
    }                                                               
                                                                    
    public void setMessage(T message) {                             
        this.message = message;                                     
    }                                                               
    public void testMethod(T t){                                    
        System.out.println(t);                                      
    }                                                               
}                                                                   
public class Cachu {                                                
    public static void main(String[] args) {                        
        Myclass<String> m1=new Myclass<>();                         
        Myclass<Integer> m2=new Myclass<>();  
        //泛型类型擦出检验方法                      
   //        //1. instanceof
//        System.out.println(message1 instanceof Message);//true
//        System.out.println(message2 instanceof Message);//true
//
//        //2. getClass
//        System.out.println(message1.getClass().getName());//message1对象实例化的类型
//        System.out.println(message2.getClass().getName());
  
    }                                                               
}                                                                   

2.对于泛型类型经过擦出以后在运行时候的类型

//class Myclass<T>{
//    private T message;
//
//    public T getMessage() {
//        return message;
//    }
//
//    public void setMessage(T message) {
//        this.message = message;
//    }
//    public void testMethod(T t){
//        System.out.println(t);
//    }
//}
//public class Cachu {
//    public static void main(String[] args) {
//        Myclass<String> m1=new Myclass<>();
//        Myclass<Integer> m2=new Myclass<>();
//      System.out.println(m1.getClass()==m1.getClass());
//    }
//}

结论:
(1)上面这种泛型的类型如果在最后没有指定具体的类型,那么在最后经过擦出以后,在运行的时候我们的类型就是Object类型。
(2)但是如果我们有上限的通配符,那么就算没有指定类型最后的类型就是上限。
四、泛型的总结

  1. 泛型类
    1.1 className<T,S> 使用时指定具体的类型
    1.2 ? extends classType 指定类型参数的上限
    1.3 ? super classType 指定类型参数下限

  2. 泛型方法
    2.1 returnValue method(T args)
    2.2 泛型方法和泛型类是相互独立
    2.3 ? , ? extends classType(不能改内容) , ? super classType(可以改内容)

  3. 泛型接口
    3.1 interfaceName
    3.2 className implements interfaceName
    3.2 className implements interfaceName
    3.3 结合匿名内部类使用

  4. 泛型擦除
    4.1 泛型信息存在于编译阶段,运行时类型擦除
    4.2 未指定类型参数上限时,类型统一设置Object
    4.3 指定类型参数上限时,类型统一设置为上限类型

猜你喜欢

转载自blog.csdn.net/ZhuiZhuDream5/article/details/84973615