泛型【Java】

泛型

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

创建一个类,可以看出数组也可以为Object类型的
在这里插入图片描述
存放数据并且获取下标元素的值
在这里插入图片描述
虽然说,这样数组可以存放任意类型的数据,但不太方便,所以我们引入了“泛型

1. 泛型类的使用

1.1 语法

泛型类<类型实参> 变量名;//定义一个泛型类引用
new 泛型类<类型实参>(构造方法实参);//实例化一个泛型类对象

1.2 示例

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

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

1.3 类型推导

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

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

然后,修改一下刚开始的代码,引入泛型:

//<T> 标志位:代表当前类是泛型类
class MyArry2<T>{
    //public Object[] array = new Object[10];->会存在一个Object的数组
    public T[] array = (T[])new Object[10];

    public T getPos(int pos){
        return array[pos];
    }
    public void setVal(int pos,T val){
        this.array[pos] = val;
    }
    public T[] getArray(){
        return array;
    }
}

<>里面指定了类型,说明此时这个类里面,只能放这个数据类型的数据
如下代码,<>中指定了String 说明只能放String类型的数据;<>中指定了Interage 说明只能放Interage类型的数据

public static void main2(String[] args){
        //1.<>里面指定了类型,说明此时这个类里面,只能放这个数据类型的数据
        MyArry2<String> myArry = new MyArry2<String>();
        myArry.setVal(0,"abc");
        myArry.setVal(1,"hello");

        String s = (String) myArry.getPos(1);
        System.out.println(s);

        MyArry2<Integer> myArry2 = new MyArry2<Integer>();
        myArry2.setVal(0,10);
        myArry2.setVal(1,13);
        myArry2.setVal(2,60);

    }

注释:

  1. 类名后面的< T >代表占位符,表示当前类是一个泛型类
    了解:【规范】类型形参一般使用一个大写字母表示,常用的名称有:
    E 表示Elenment
    K 表示Key
    V 表示Value
    N 表示Number
    T 表示Type
    S,U,V等等-第二,第三,第四个类型
  2. 当指定类型之后,编译器会根据你指定的类型参数来进行类型的检查
    myArray.setVal(1,"hello");
  3. 当取元素的时候,不需要再进行强制类型转换了
  4. java中,不可以new泛型类型的数组,会报错
    在这里插入图片描述
  5. 注意<>中必须是引用类型
    在这里插入图片描述

2.泛型如何编译?

2.1 擦除机制

需要安装一个插件jclasslib Bytecode Viewer
在这里插入图片描述
在这里插入图片描述
安装完成后
在这里插入图片描述
在这里插入图片描述
擦除机制就是在编译的过程中,将泛型T替换为Object,并且擦除机制就是编译十七的一种机制,运行期间没有泛型这个概念。

3.裸类型(Raw Type) 【了解】

裸类型是一个泛型类 但没有带着实参类型 ,下方代码就是一个裸类型

在这里插入图片描述
注意:我们不要自己去使用裸类型,裸类型是为了兼容老版本的API保留的机制。

4.泛型的边界

只有泛型的上界,没有泛型的下界

4.1 示例

语法格式

class 泛型类名称< 类型形参 extends 类型边界 >{... ..}

在这里插入图片描述
在这里插入图片描述
了解:没有指定类型边界 E,可以视为 E extends Object

4.2 复杂示例

写一个泛型类,找出数组当中的最大值,只要这个T,实现类这个接口就行
在这里插入图片描述
这里的 T 是引用数据类型,直接比较不了,所以引用类型比较要用比较器
完整代码如下

class Alg<T extends Comparable<T>>{
    public T findMaxVal(T[] array){
        T maxVal = array[0];
        for (int i = 1; i<array.length; i++){
            if(array[i].compareTo(maxVal)>0){
                maxVal = array[i];
            }
        }
        return maxVal;
    }

}
public class Test {
	public static void main(String[] args) {
        Alg<Integer> alg = new  Alg<>();
        Integer[] array = {78,23,109,2,9,10};
        int val = alg.findMaxVal(array);
        System.out.println(val);
    }
}

运行结果:
在这里插入图片描述

5.泛型方法

语法格式

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

在这里插入图片描述
上面的例子在数组中找最大值,有两种方法
静态方法

class Alg2{
    //静态方法
    public static<T extends Comparable<T>> T findMaxVal(T[] array){
        T maxVal = array[0];
        for (int i = 1; i<array.length; i++){
            if(array[i].compareTo(maxVal)>0){
                maxVal = array[i];
            }
        }
        return maxVal;
    }
}
public class Test3 {
	public static void main2(String[] args) {
        Integer[] array = {78,23,109,2,9,10};
        //int val = Alg2.<Integer>findMaxVal(array);
        //可以省略泛型类型
        int val = Alg2.findMaxVal(array);
        System.out.println(val);
    }
}

使用静态方法后,不需要引用对象,可以不用写明类型,现在可以根据实参类型推导出类型是什么。

成员方法

class Alg3{
    //泛型方法:成员方法
    public <T extends Comparable<T>>T findMaxVal2(T[] array){
        T maxVal = array[0];
        for (int i = 1; i<array.length; i++){
            if(array[i].compareTo(maxVal)>0){
                maxVal = array[i];
            }
        }
        return maxVal;
    }
}
public class Test3 {
	public static void main(String[] args) {
        Alg3 alg3 = new Alg3();
        Integer[] array = {78,23,109,2,9,10};
        int val = alg3.findMaxVal2(array);
        System.out.println(val);
    }
}

因为这个不是静态方法,所以还是要引用对象。

6.通配符(?)

通配符 “?” 表示可以接收任意类型数据

6.1 上界通配符<? extends 实参类型>

上界:用 extends 关键字声明,表示参数化的类型可能是所指定的类型,或者是此类型的子类。

语法
类型1指定一个数据类型,那么类型2就只能是类型1或者类型1的子类

Vector<? extends 类型1> x = new Vector<类型2>();

在类型参数中使用extends表示这个泛型中的参数必须是 E 或者 E 的子类,这样有两个好处:

  1. 如果传入的类型不是 E 或者 E 的子类,编译不成功
  2. 泛型中了一使用 E 的方法,要不然还得强转成 E 才能使用

看个例子:

class Food{ }
class Fruit extends Food{ }
class Apple extends Fruit{ }
class Banana extends Fruit{ }
class Test <T>{
    private T val;
    public T getVal(){
        return val;
    }
    public void setVal(T val){
        this.val = val;
    }
}
public class Test1 {
	public static void main1(String[] args) {
	        Test<Apple> test1 = new Test<>();
	        test1.setVal(new Apple());
	
	        fun(test1);
	
	        Test<Banana> test2 = new Test<>();
	        test2.setVal(new Banana());
	
	        fun(test2);
	    }
	    //通配符的上界,这里只要是Fruit或者Fruit的子类即可
	    public static void fun(Test<? extends  Fruit> temp){
	        /*
	        这里无法确定 temp 引用的是那个子类对象,所以无法进行修改
	        temp.setVal(new Banana());
	        temp.setVal(new Apple());
	    */
	        //放的都是Fruit或者Fruit的子类,所以可以直接使用 Fruit 接收
	        Fruit banana = temp.getVal();
	        System.out.println(temp.getVal());
	    }
}

6.2 下界通配符<? super 实参类型>

下界:用 super 进行声明,表示参数化的类型可能是所指定的类型,或者是此类型的父类型,直至Object

语法
类型1指定一个数据类型,那么类型2就只能是类型1或者是类型1的父类

Vector<? super 类型1> x = new Vector<类型2>();

看个例子:

class Food{ }
class Fruit extends Food{ }
class Apple extends Fruit{ }
class Banana extends Fruit{ }
class Test <T>{
    private T val;
    public T getVal(){
        return val;
    }
    public void setVal(T val){
        this.val = val;
    }
}
public class Test1 {
	public static void main2(String[] args) {
        Test<Fruit> test1 = new Test<>();
        test1.setVal(new Fruit());
        fun2(test1);
        Test<Food> test2 = new Test<>();
        test2.setVal(new Food());
        fun2(test2);
    }
    //通配符的下界,一般用来添加元素,添加的是Fruit或者Fruit的子类
    public static void fun2(Test<? super  Fruit> temp){
        temp.setVal(new Banana());
        temp.setVal(new Apple());
        temp.setVal(new Fruit());
        System.out.println(temp.getVal());//只能直接输出
    }
}

6.3 小结

  1. 在通配符中既有上界,又有下界
  2. 通配符的上界中,不能进行数据的写入,只能进行数据的读取
  3. 通配符的上界中,不能进行数据的读取,只能进行数据的写入

" ? " 和 " T " 的区别

  1. 我们可以对 T 进行操作,但是对 ?不行,比如下面两种:
    在这里插入图片描述
  2. T 是一个确定的类型,通常用于泛型类和泛型方法的定义;
    ? 是一个不确定的类型,通常用于泛型方法的调用代码和形参,不能用于定义类和泛型方法

7.包装类

7.1 基本数据类型对应包装类

基本数据类型包装类
byteByte
shortShort
intInterage
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean

7.2 装箱和拆箱

装箱

将基本数据类型转换为包装类类型
装箱调用Interage.valueOf方法

public static void main3(String[] args) {
        int a = 10;
        Integer integer = a;//自动 装箱->底层调用的还是Interage.valueOf

        Integer integer2 = new Integer(a);//显示 装箱

        Integer ii = Integer.valueOf(a);//显示 装箱

        System.out.println(integer);
        System.out.println(integer2);
        System.out.println(ii);
    }

拆箱

将包装类类型转换为基本数据类型
拆箱调用Interage.intValue方法

public static void main4(String[] args) {
        int a = 10;
        Integer integer = a;

        int val = integer;//自动 拆箱->底层调用的还是intValue
        System.out.println(integer);

        int val2 = integer.intValue();//显示 拆箱
        System.out.println(val2);

        double val3 = integer.doubleValue();//显示 拆箱
        System.out.println(val3);
    }
  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值