Java泛型

泛型,实质上就是一个占位符(T,W,E)

 

目录

1.普通的泛型类

2.泛型方法,包括静态的

3.泛型接口

4.泛型擦除模式

5.泛型通配符

6.泛型上边界,下边界


1.普通的泛型类

List<Integer> list = new ArrayList<Integer>();

这里给list集合指定了一个数据类型,这就是泛型的应用。这里指定了Integer类型的数据,则不允许ArrayList里有String类型的数据添加进去。

举个例子:

先写一个封装好的外部实体类Person,然后在main方法里正常调用,这是很常见的写法。

public class Demo{
    public static void main(String args[]){
        Person p = new Person();
        p.setName("小明");
        System.out.print(p.getName());
    }
}

class Person {
    private String name;
    private String address;
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
}

 

如果说在不知道未来name和address的类型的时候,这个时候可以使用类型占位符来进行占位
定义一个泛型类,实质就是将现在代码中所有的特定的类型换成占位符TW E。

根据上面的代码,修改为泛型类如下:

public class Demo{
    public static void main(String args[]){
        //规定占位符为String类型,则Person里的所有属性以及get/set方法均为String类型
        Person<String> p = new Person<String>();  
        p.setName("小明");
        System.out.println(p.getName());
        
        //同理规定占位符为Integer类型,则Person里的所有属性以及get/set方法均为Integer类型
        Person<Integer> p2 = new Person<Integer>();  
        p2.setName(2333);
        System.out.print(p2.getName());
    }
}

class Person<T> {
    private T name;
    private T address;
    
    public T getName() {
        return name;
    }
    public void setName(T name) {
        this.name = name;
    }
    public T getAddress() {
        return address;
    }
    public void setAddress(T address) {
        this.address = address;
    }
}

上面的例子只是定义了一个占位符,如果有两个呢,而且是不同类型呢,玩法如下

public class Demo{
    public static void main(String args[]){
        Person<String,Integer> p = new Person<String,Integer>();  
        p.setName("小明");
        p.setAddress(2333);
        System.out.println(p.getName()+p.getAddress());
    }
}

class Person<K,V> {
    private K name;
    private V address;
    public K getName() {
        return name;
    }
    public void setName(K name) {
        this.name = name;
    }
    public V getAddress() {
        return address;
    }
    public void setAddress(V address) {
        this.address = address;
    }
}

这个占位符的个数可以是多个,没有限制,我猜的。

 

2.泛型方法,包括静态的

如果不能确定一个方法的形参类型,可以使用泛型。
静态泛型方法中的类型占位符和类中的泛型占位符是没有关系的。
因为泛型类针对的是对象,而泛型静态方法针对的是整个类。对象与类的区别参照Java面向对象基础总结里的Java面向对象。

正常的写法如下:
public class Demo{
    public static void main(String args[]){
        Person p = new Person();  
        p.show("张三");
    }
}

class Person {
    public void show(String name){
        System.out.println(name+"正在演讲");
    }
}

如果用泛型呢:

public class Demo{
    public static void main(String args[]){
        Person<String> p = new Person<String>();  
        p.show("张三");
        p.show1("张三");
        System.out.println(p.show2("张三"));
        p.show3("张三");
    }
}

class Person<T> {
    public void show(T name){
        System.out.println(name+"正在演讲");
    }
    
    //普通方法的占位符也可以类的占位符也可以不一致
    public <M> void show3(M name){
        System.out.println(name+"普通方法");
    }

   //这里是static修饰的,所有它的占位符与类的占位符没有关系,不可重名
    public static <W> void show1(W name){   
        System.out.println(name+"静态方法正在演讲");
    }
    
    public static <E> E show2(E name){
        return name;
    }
}

 

3.泛型接口

泛型接口的实现类可以指定具体的泛型接口的具体泛型的类型

public class Demo {
    public static void main(String[] args) {
        Student student = new Student();
        student.show("张三");
    }
}

interface Teacher<T>{
    public void show(T name);
}

class Student implements Teacher<String>{  //继承类的将占位符定义为String类型
    @Override
    public void show(String name) {
        System.out.println("我是"+name);
    }
}

泛型接口的实现类如果没有指定具体的泛型泛型,必须要在这个实现类中声明一个泛型类型占位符给接口用,则写法如下

public class Demo {
    public static void main(String[] args) {
        Student student = new Student();
        student.show("张三");
    }
}

interface Teacher<T>{
    public void show(T name);
}

class Student <T> implements Teacher<T>{  //继承类的将占位符定义为String类型
    @Override
    public void show(T name) {
        System.out.println("我是"+name);
    }
}

 

4.泛型擦除模式

java中的泛型只存在于编码编译阶段,在运行期间泛型的类型是会被去除掉的。

擦除模式实质就是在代码运行期间将所有的泛型全部去掉。

为什么要用擦除模式:为了兼容JDK老版本的编码。

public class Demo {
    public static void main(String[] args) {
        //相同占位符的泛型类实例化比较,这是编译阶段
        Student<String> student = new Student<String>();
        Student<String> student1 = new Student<String>();
        System.out.println(student.getClass()==student1.getClass()); //true
        //不同占位符的泛型类实例化比较,这是编译阶段
        Student<String> student2 = new Student<String>();
        Student<Integer> student3 = new Student<Integer>();
        System.out.println(student2.getClass()==student3.getClass()); //true
        //这是运行阶段或者是字节码阶段
        Student student4 = new Student();
        Student student5 = new Student();
        System.out.println(student4.getClass()==student5.getClass()); //true
    }
}

class Student<T>{  

}

 

5.泛型通配符

java中的继承并不是泛 型中的继承,java中的父子类关系在泛型中 并不是父子类关系。
通配符:由于java中继承关系,在泛型中不做任何声明修饰的情况写下是不被认可的,所以要使用通配符进行进行处理;
接下来会使用通配符在泛型中将java中的继承关系重新绑定;
通配符一般使用?来表示 可以理解为? 在泛型中所有类的父类。

Integer继承自Number类

public class Demo{
    public static void main(String[] args) {
        Person<Number> p = new Person<Number>();
        Person<Integer> p1 = new Person<Integer>();
        p.show(p1); //这里报错
        /*
        尽管Integer继承自Number,但是这里是泛型类,继承关系不适用泛型类,否则这里也不会报错,
        所以要使用通配符解决这个问题
         */
    }
}

class Person<T> {
    private T name;
    public T getName() {
        return name;
    }
    public void setName(T name) {
        this.name = name;
    }
    public void show(Person<T> ps){
        System.out.println("你好世界");
    }
}

使用通配符解决,show方法改为如下:

public class Demo{
    public static void main(String[] args) {
        Person<Number> p = new Person<Number>();
        Person<Integer> p1 = new Person<Integer>();
        p.show(p1); //这里不会报错了

        Person<String> p2 = new Person<String>();
        p.show(p2);  //由于?通配符是所有泛型类的父类,所以这里不会报错,但是这样也有问题,p的占位符是number类型,但是传了String却不报错,这是个问题,此代码知识点6来解决。
    }
}

class Person<T> {
    private T name;
    public T getName() {
        return name;
    }
    public void setName(T name) {
        this.name = name;
    }
    //由于这里使用了通配符,适用于所有类型,所以使用String也不会报错
    public void show(Person<?> ps){
        System.out.println("你好世界");
    }
}

 

6.泛型上边界,下边界

上边界代表泛型类继承哪个父类---extends,? extends T代表泛型可以传入T类和T类的子类的类型:

public class Demo{
    public static void main(String[] args) {
        Person<Number> p = new Person<Number>();
        Person<Integer> p2 = new Person<Integer>();
        p.show(p2);  //不报错,因为Integer继承Number,所以p可以传入泛型子类p2
    }
}

class Person<T> {
    private T name;
    public T getName() {
        return name;
    }
    public void setName(T name) {
        this.name = name;
    }
    public void show(Person<? extends T> ps){  //定义上边界
        System.out.println("你好世界");
    }
}

下边界 ? super T 代表的是传入的必须是T和T的父类类型:

public class Demo{
    public static void main(String[] args) {
        Person<Number> p = new Person<Number>();
        Person<Integer> p2 = new Person<Integer>();
        p2.show(p);  //这里传入的是泛型父类的类型,即Number类型
    }
}

class Person<T> {
    private T name;
    public T getName() {
        return name;
    }
    public void setName(T name) {
        this.name = name;
    }
    public void show(Person<? super T> ps){  //定义下边界
        System.out.println("你好世界");
    }
}

上边界与下边界都是什么情况下使用:
上边界在读取T这个类型数据的时候,但不写入数据的时候使用上边界,因为继承自父类T,只能看读取父类T,不可操作父类T,所以无法写入数据。
下边界需要写入数据的时候,单不需要读取的时候使用下边界。

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值