【Java】泛型2

简介: 【Java】泛型

5.通配符

通配符:?用于在泛型的使用,这个?就是通配符


5.1通配符的作用

class Array<T> {
    public T[] arr = (T[])(new Object[10]);
    public void setArr(T val,int pos) {
        this.arr[pos] = val;
    }
    public T getArr(int pos) {
        return this.arr[pos];
    }
}
public class Test {
    public static void main(String[] args) {
        Array<Integer> array = new Array<Integer>();
        fun(array);
    }
    public static void fun(Array<Integer> arr) {
        arr.setArr(1,0);
        System.out.println(arr.getArr(0));
    }
}


在主函数中实例化了一个 Array 泛型类,传的类型是 Integer。在 fun 方法形参中只能接收 Array 类的类型为 Integer 的对象,调用 fun 方法可以使用类型推导,如果传入的不是 Array 类的类型为Integer 的对象则会报错。Array 是一个泛型类,实例化的时候可以传入不同类型的实参,那么 fun这样就有了一定的局限性


那么此时我们就可以将fun方法形参中的Array<>里面类型设置为通配符,这样就可以接收 Array 类的任何类型的对象了


class Array<T> {
    public T[] arr = (T[])(new Object[10]);
    public void setArr(T val,int pos) {
        this.arr[pos] = val;
    }
    public T getArr(int pos) {
        return this.arr[pos];
    }
}
public class Test {
    public static void main(String[] args) {
        Array<Integer> array = new Array<Integer>();
        array.setArr(1,0);
        fun(array);
    }
    public static void fun(Array<?> arr) {
        System.out.println(arr.getArr(0));
    }
}


使用通配符"?"说明它可以接收任意类型的Array对象,但是由于不确定类型,所以无法修改


5.2子通配符

在通配符的基础上又产生了两个子通配符:


?extends 类:设置泛型上界

?super 类:设置泛型下界

5.2.1通配符上界

①语法


<? extends 上界>
<? extends Number>

传入的实参类型必须是 Number 本身类或者是 Number 的子类


②示例


class Fruits {
}
class Apple extends Fruits {
}
class Message<T> {
    private T message;
    public void setMessage(T message) {
        this.message = message;
    }
    public T getMessage() {
        return message;
    }
}
public class Main {
    public static void main(String[] args) {
        Message<Apple> message = new Message<>();
        message.setMessage(new Apple());
        fun(message);
    }
    public static void fun(Message<? extends Fruits> tmp) {
        //tmp.setMessage(new Apple());
        Fruits fruits = tmp.getMessage();
    }
}


fun 形参类型传过来的实参类型必须是 Fruits 类型 或者是 Fruits 子类类型。在fun里面用通配符接收类型,那么我们也就不清楚是具体的哪种类型,只知道是Fruits 类型 或者是 Fruits 子类类型,那么我们就无法调用 setMessage 去设置 message 的值,因为子类类型无法存储父类对象。我们知道主函数传给fun方法的对象类型要么是 Fruits 类型 要么是 Fruits 子类类型,那么我们就可以调用 getMessage 去获取 message 的值,用Fruits 接收即可。


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


5.2.2通配符的下界

①语法


<? super 下界>
<? super Integer>

传过来的类型必须是 Integer 本身类 或者是 Integer 的父类


②示例


class Fruits {
}
class Apple extends Fruits {
}
class Message<T> {
    private T message;
    public void setMessage(T message) {
        this.message = message;
    }
    public T getMessage() {
        return message;
    }
}
public class Main {
    public static void main(String[] args) {
        Message<Fruits> message = new Message<>();
        message.setMessage(new Fruits());
        fun(message);
    }
    public static void fun(Message<? super Fruits> tmp) {
        tmp.setMessage(new Apple());
        //Fruits fruits = tmp.getMessage();
    }
}


fun 形参类型传过来的实参类型必须是 Fruits 类型 或者是 Fruits类型的父类,那我们在fun方法中通过 setMessage 去设置 message 的值时,我们知道主函数传给 fun 方法的对象类型最低也得是Fruits 方法,通过 setMessage 去设置 message 值是我们可以设置为 Fruits 子类对象,因为 Fruits子类肯定也是Fruits父类的子类,所以可以设置 message 的值。但是不能通过 getMessge 去获取messge的值,原因就在于我们只知道主函数传给fun实参类型是 Fruits 类型 或者是 Fruits类型的父类,如果传过来的实参类型是 Fruits 父类那我们去获取message的值时我们就不知道用什么接收了。


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


6.包装类

在 Java 中,基本类型是不会继承 Object 类的,为了在泛型代码中可以支持基本类型,Java给每个基本类型都对应了一个包装类型


6.1基本类型对应的包装类

基本数据类型 包装类

基本数据类型 包装类
byte Byte
shout Shout
int Integer
long Long
float Float
double Double
char Character
boolean Boolean

6.2装箱和拆箱

6.2.1装箱

int a = 10;
Integer i = Integer.valueOf(a);

请.png


Integer.valueof:主要将一个基本数据类型的值存放到 Integer 类的对象里面


上述代码装箱操作,新建一个 Integer 类型对象,将 i 的值放入对象的某个属性中


Integer 类中的 valueOf 方法中,如果你存储的值在 -128(IntegerCache.low:-128)到127(IntegerCache:127)范围中就存储到 i +(-IntegerCache.low)这个位置的数组当中。如果超出了这个范围就 new 一个 Integer 对象,通过构造方法存储在 value 属性中


6.2.2拆箱

int a = 10;
Integer i = Integer.valueOf(a);//装箱
int b = i.intValue();//拆箱

拆箱操作,将 Integer 对象中的值取出,放到一个基本数据类型中


6.2.3自动装箱和自动拆箱

从上述手动装箱和拆箱中我们可以看出手动装箱和拆箱给开发者带来了不少代码量,为了减少开发者的负担,Java提供了自动装箱拆箱机制


public class Demo {
    public static void main(String[] args) {
        int i = 10;
        Integer ii = i; // 自动装箱
        Integer ij = (Integer)i; // 自动装箱
        int j = ii; // 自动拆箱
        int k = (int)ii; // 自动拆箱
    }
}

去.png


通过字节码文件可以看出在编译时期会自动的装箱拆箱,还原为装箱和拆箱



相关文章
|
3月前
|
安全 Java 编译器
揭秘JAVA深渊:那些让你头大的最晦涩知识点,从泛型迷思到并发陷阱,你敢挑战吗?
【8月更文挑战第22天】Java中的难点常隐藏在其高级特性中,如泛型与类型擦除、并发编程中的内存可见性及指令重排,以及反射与动态代理等。这些特性虽强大却也晦涩,要求开发者深入理解JVM运作机制及计算机底层细节。例如,泛型在编译时检查类型以增强安全性,但在运行时因类型擦除而丢失类型信息,可能导致类型安全问题。并发编程中,内存可见性和指令重排对同步机制提出更高要求,不当处理会导致数据不一致。反射与动态代理虽提供运行时行为定制能力,但也增加了复杂度和性能开销。掌握这些知识需深厚的技术底蕴和实践经验。
78 2
|
24天前
|
Java API
[Java]泛型
本文详细介绍了Java泛型的相关概念和使用方法,包括类型判断、继承泛型类或实现泛型接口、泛型通配符、泛型方法、泛型上下边界、静态方法中使用泛型等内容。作者通过多个示例和测试代码,深入浅出地解释了泛型的原理和应用场景,帮助读者更好地理解和掌握Java泛型的使用技巧。文章还探讨了一些常见的疑惑和误区,如泛型擦除和基本数据类型数组的使用限制。最后,作者强调了泛型在实际开发中的重要性和应用价值。
20 0
[Java]泛型
|
1月前
|
存储 安全 Java
🌱Java零基础 - 泛型详解
【10月更文挑战第7天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
11 1
|
1月前
|
Java 语音技术 容器
java数据结构泛型
java数据结构泛型
27 5
|
1月前
|
存储 Java 编译器
Java集合定义其泛型
Java集合定义其泛型
18 1
|
2月前
|
Java 编译器 容器
Java——包装类和泛型
包装类是Java中一种特殊类,用于将基本数据类型(如 `int`、`double`、`char` 等)封装成对象。这样做可以利用对象的特性和方法。Java 提供了八种基本数据类型的包装类:`Integer` (`int`)、`Double` (`double`)、`Byte` (`byte`)、`Short` (`short`)、`Long` (`long`)、`Float` (`float`)、`Character` (`char`) 和 `Boolean` (`boolean`)。包装类可以通过 `valueOf()` 方法或自动装箱/拆箱机制创建。
37 9
Java——包装类和泛型
|
1月前
|
存储 Java 编译器
【用Java学习数据结构系列】初识泛型
【用Java学习数据结构系列】初识泛型
20 2
|
2月前
|
安全 Java API
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
String常量池、String、StringBuffer、Stringbuilder有什么区别、List与Set的区别、ArrayList和LinkedList的区别、HashMap底层原理、ConcurrentHashMap、HashMap和Hashtable的区别、泛型擦除、ABA问题、IO多路复用、BIO、NIO、O、异常处理机制、反射
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
|
1月前
|
安全 Java 编译器
Java基础-泛型机制
Java基础-泛型机制
16 0
|
2月前
|
存储 安全 搜索推荐
Java中的泛型
【9月更文挑战第15天】在 Java 中,泛型是一种编译时类型检查机制,通过使用类型参数提升代码的安全性和重用性。其主要作用包括类型安全,避免运行时类型转换错误,以及代码重用,允许编写通用逻辑。泛型通过尖括号 `&lt;&gt;` 定义类型参数,并支持上界和下界限定,以及无界和有界通配符。使用泛型需注意类型擦除、无法创建泛型数组及基本数据类型的限制。泛型显著提高了代码的安全性和灵活性。