(Java)泛型进阶

注:此博文为本人学习过程中的笔记

1.什么是泛型

一般的类和方法,只能使用具体的类型:要么是基本类型,要么是自定义的类。如果要编写可以应用于多种类型的代码,这种刻板的限制对代码的束缚就会很大

泛型是在JDK1.5引入的新语法,通俗讲,泛型:就是适用于许多类型。从代码上将,就是对类型实现了参数化

2.引出泛型 

实现一个类,类中包含一个数组成员,使得数组中可以存放任何类型的数据,也可以根据成员方法返回数组某个下标的值

思路:

1.我们以前学过的数组,只能存放指定类型的元素

2.所有类的父类,默认为Object类

class MyArray {
    public Object[] array = new Object[10];
    public Object getPos(int pos) {
        return this.array[pos];
    }
    public void setVal(int pos, Object val) {
        this.array[pos] = val;
    }
}
public class testDemo {
    public static void main(String[] args) {
        MyArray myArray = new MyArray();
        myArray.setVal(0, 10);
        maArray.setVal(1, "hello");
        String ret = myArray.getPos(1);//编译报错
        System.out.println(ret);
    }
}

问题:

1.任何类型数据都可以存放

2.1号下标本身就是字符串,但是却编译报错,必须进行强制类型转换

虽然在这种情况下,当前数组任何数据都可以存放,但是,更多情况下,我们还是希望它只能够持有一种数据类型。而不是持有这么多类型。所以,泛型的主要目的:就是指定当前的容器,要持有什么类型的对象。让编译器去做检查。此时,就需要把类型,作为参数传递。需要什么类型就传入什么类型 

2.1.语法 

class 泛型类名称<类型形参列表> {
    //这里可以使用类型参数
}

class 泛型类名称<类型形参列表> extends 继承类/*这里可以使用类型参数*/ {
    //这里可以使用类型参数
}

class className(T1, T2, ..., Tn) extends ParentClass<T1> {
    //可以只使用部分类型参数
}

上述代码进行如下改写

class MyArray<T> {
    public Object[] array = new Object[10];
    public T getPos(int pos) {
        return (T)this.array[pos];
    }
    public void setVal(int pos, T val) {
        this.array[pos] = val;
    }
}
public class TestDemo {
    public static void main(String[] args) {
        MyArray<Integer> myArray = new MyArray<>();//1
        myArray.setVal(0, 10);
        myArray.setVal(1, 12);
        int ret = myArray.getPos(1);//2
        System.out.println(ret);
        myArray.setVal(2, "bit");//3
    }
}

代码解释:

1.类名后的<T>代表占位符,表示当前类是一个泛型类

类型形参一般使用一个大写字母表示,常用的名称有:

E 表示 Element

K 表示 Key

V 表示 Value

N 表示 Number

T 表示 Type

S, U, V 等等-第二,第三,第四个类型

2.类型后加入<Integer>指定当前类型

3.不需要进行强制类型转换

3.泛型类的使用

3.1.语法 

泛型类<类型实参> 变量名;        //定义一个泛型类引用

new 泛型类<类型实参>(构造方法实参);        //实例化一个泛型类对象

3.2.示例 

MyArray<Integer> list = new MyArray<Integer>();

注意:泛型只能接受类,所有的基本数据类型必须使用包装类 

3.3.类型推导 

当编译器可以根据上下文推导出类型实参,可以省略类型实参的填写

MyArray<Integer> list = new MyArray<>();//可以推导出实例化需要的类型实参为Integer 

4.裸类型 

裸类型是一个泛型类当没有带着类型实参,例如MyArrayList是一个裸类型

MyArray list = new MyArray();

注意:我们不要自己去使用裸类型,裸类型是为了兼容老版本的API保留的机制 

5.泛型是如何编译的 

5.1.擦除机制 

泛型本质是一个非常难的语法

在编译过程当中,将所有的T替换为Object这种机制,我们称为擦除机制

Java的泛型机制是在编译级别实现的,编译器生成的字节码在运行期间并不包含泛型的类型信息 

6.泛型的上界 

在定义泛型类是,有时需要对传入的类型变量做一定的约束,可以通过类型边界来约束

6.1.语法 

class 泛型类名称<类型实参 extends 类型边界> {

}

6.2.示例

pulic class MyArray<E extends Number> {

}

只接受Number的子类型作为E的类型实参

MyArray<Integer>        //正常,因为Integer是Number的子类型

MyArray<String>        //编译错误,因为String不是Number的子类型

没有指定类型边界E,可以视为E extends Object 

6.3.复杂示例 

public class MyArray<E extends Comparable<E>> {

}

E必须是实现了Comparable接口的

7.泛型方法 

7.1.定义语法 

方法限定符 <类型形参列表> 返回值类型 方法名称(形参列表) {...} 

7.2.示例 

public class Util {
    //静态的泛型方法需要在statci后面<>声明泛型类型参数
    public static <E> void swap(E[] array, int i, int j) {
        E t = array[i];
        array[i] = array[i];
        array[i] = t;
    }
}

7.3.使用实例-可以类型推导 

Integer[] a = {...};
swap(a, 0, 9);

String[] b ={...};
swap(b, 0, 9);

7.4.使用实例-不使用类型推导 

Integer[] a = {...};
Util.<Integer>swap(a, 0, 9);

String[] b = {...};
Util.<String>swap(b, 0, 9);

8.通配符 

?用于在泛型的使用,即为通配符

8.1.通配符解决什么问题

示例:

class Message<T> {
    private T message;
    public T getMessage() {
        return message;
    }
    public void setMessage(T message) {
        this.message = message;
    }
}
public class TestDemo {
    public static void main(String[] args) {
        Message<String> message = new Message<>();
        message.setMessage("hello world");
        fun(message);
    }
    public static void fun(Message<String> temp) {
        System.out.println(temp.getMessage());
    }
}

以上程序会带来新的问题,如果现在泛型的类型设置的不是String,而是Integer

public class TestDemo {
    public static void main(String[] args) {
        Message<Integer> message = new Message();
        message.setMessage(99);
        fun(message);//出现错误,只能接受String
    }
    public static void fun(Message<String> temp) {
        System.out.println(temp.getMessage());
    }
}

需要的解决方案:可以接受所有的泛型类型,但是又不能够让用户随意修改,这种情况就需要使用通配符"?"处理

public class TestDemo {
    public static void main(String[] args) {
        Message<Integer> message = new Message();
        message.set.Message(55);
        fun(Message);
    }
    //此时使用通配符"?"描述的是它可以接受任意类型,但是由于不确定类型,所以无法修改
    public static void fun(Message<?> temp) {
        System.out.println(temp.getMessage());
    }
}

在"?"的基础又产生了两个子通配符

? extends 类:设置通配符上限

? super 类:设置通配符下限 

8.2.通配符上界 

语法:

<? extends 上界>

<? extends Number>//可以传入的实参类型是Number或者Number的子类 

通配符的上界,不能进行写入数据,只能进行读取数据 

8.3.通配符下界 

<? super 下界>

<? super Integer>//代表可以传入的实参的类型是Integer或者Integer的父类

通配符的下界不能进行读取数据,只能写入数据 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值