泛 型

本文深入探讨了Java中的泛型,包括泛型的概念、引入原因、语法以及泛型类的实例化。讲解了泛型在数组操作中的类型检查和转换优势,以及泛型的擦除机制。通过示例展示了泛型方法的设计,泛型的上界和下界,以及通配符的使用。通配符允许更灵活的数据读取和写入。整个讨论旨在提高代码的复用性和安全性。
摘要由CSDN通过智能技术生成

集合的背后----》数据结构:描述和组织数据的。

1.1 泛型的概念

一般的类和方法,只能使用具体的类型: 要么是基本类型,要么是自定义的类。如果要编写可以应用于多种类型的代码,这种刻板的限制对代码的束缚就会很大。----- 来源《Java编程思想》对泛型的介绍。泛型是在JDK1.5引入的新的语法,通俗讲,泛型:就是适用于许多许多类型。从代码上讲,就是对类型实现了参数化。


1.2 引出泛型

问题:实现一个能够存放任何类型的数组。

 问题:此代码维护成本高,虽然可以存放多种类型,但只是方便了存数据,对取数据造成了极大的麻烦,每取一个数据还得进行强转。

为了解决问题,故引出泛型。


1.3 泛型语法 

class 泛型类名称<类型形参列表> {
  // 这里可以使用类型参数
}
class ClassName<T1, T2, ..., Tn> { 
}

现在我们可以改良一下上面的代码

在类名后面用接括号中包含泛型‘T’(也可以别的),将全篇所有的Object更换为T,用的时候直接按数据基本类型存和拿取,即达成了目的,又使得数据更加简洁,维护成本低。 

泛型两个最大的意义:

1. 存放数组时会进行类型的检查。

2. 取出数据时泛型会自动帮你转换数据的类型,没有必要再进行强转了。

并且泛型主要是编译时期的一种机制,这种机制的主要表现方式是擦除机制。

通常:

E 表示 Element
K 表示 Key
V 表示 Value
N 表示 Number
T 表示 Type

T[] array = (T[])new Object[10];是否就足够好,答案是未必的。这块问题一会儿介绍。


1.4 类型推导

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

2. 泛型是如何编译的

2.1 擦除机制

就是在编译的时候把所以的T替换成了Object

泛型的主要意义在于转换和检查:如上面那串代码,在编译时期,所有泛型所代表的<T>被擦除机制替换为了Object,而在使用使< >接括号中是什么类型,编译期间就会检查存放数据类型是否与其相同。


3. 如何实例化数组类数组

直接利用泛型类实例化是错误的

因为根据擦除机制,编译时会将T成Object,而Object无法用Integer接收。

 public MyArray(Class<T> clazz, int capacity) {
    array = (T[])Array.newInstance(clazz, capacity);
 }

4. 利用泛型设计一个找最大数的程序

默认最大值为arr[0]将arr[0]与数组中的其他数字比较,如果小将将这个数更换为max。

注意这里的if()括号中不能直接放布尔类型,因为这里的max与arr[i]都是<T>类型,属于引用类型无法直接比较需要用compareTo()接口来比较,但 泛型<T>的擦除机制擦成的Object没有引用

compareTo()接口需要手动进行连接,故引出泛型的上界机制。

class Alg<T extends Comparable<T>>

另外,泛型是可以实例自定义类的,

class Person implements Comparable<Person>只要该类连接了和主类一样的接口。

拿上面的程序举例,

class Alg<T extends Comparable<T>>{
    public T findMax(T[] arr){
        T max=arr[0];
        for (int i = 1; i < arr.length; i++) {
            if(max.compareTo(arr[i])<0){
                max=arr[i];
            }
        }
        return max;
    }
}
class Person implements Comparable<Person>{
    public int age;

    public Person(int age) {
        this.age = age;
    }

    @Override
    public int compareTo(Person o) {
        return this.age-o.age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                '}';
    }
}
public class Test{
    public static void main(String[] args) {
        Alg<Person> person=new Alg<>();
        Person[] people={new Person(15),new Person(20)};
        System.out.println(person.findMax(people));
    }
    public static void main2(String[] args) {
        Alg<Integer> max=new Alg<>();
        Integer[] arr={1,2,3,4,5,6,55};
        System.out.println(max.findMax(arr));
    }
}

5. 泛型的上界

如上面:的叫做泛型的上界。

泛型上界的语法:

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

<E extends Number>中的E一定是Number的子类或者Number本身。

就是说在main方法中转换泛型类型之时可以传Number本身或者Number的子类,如:

 泛型是没有下界的。

注意:泛型类是不可以加static的,因为加了static就会在代码编译过程中运行而这时还没有进入实例化步骤,自然也不能将实例化中的<>接括号中的要求转换的类型带入编码。


6. 泛型方法

但我们要思考,如果静态类传参不依赖实例化对象,我们要如何传参呢?

 我们就可以在static后面用接括号传参,此时这个方法就变为了泛型方法,而此时该类已经不是泛型类了。

而此时,在脱离了实例化对象的过程中,我们可以可以将传参放到调用方法前面 。

 如果我们不行要泛型类却需要泛型方法,则可以这样:

 将接括号放到返回类型的前面,依然可以完成实例化对象的传参。

这样的话,不需要类型的转换即可完成传参。

但如果需要也就可以加上。


 7. 通配符

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());
}
}

这样一串代码,如果现在传的参数不是字符串而是Integer类型,那是否要更改fun方法中的String类型呢?有没有一种方法可以尽量简便处理。

这里就要引入通配符了:

 在编译器看到的两串代码都是上面那样的,所以说如果同时存在只改变类型的重载,系统会报错。

 通配符相当于替代了空白位置。


7. 2 通配符的上界

public static void fun(Message<? extends Fruit> temp){
  //temp.setMessage(new Banana()); //仍然无法修改!
//temp.setMessage(new Apple()); //仍然无法修改!
  Fruit b = temp.getMessage();
  System.out.println(b);
}

 通配符的上界无法进行数据的输入,因为无法确定类型,只能进行数据的读取。


7.3 通配符的下界

 

public static void fun(Message<? super Fruit> temp){
// 此时可以修改!!添加的是Fruit 或者Fruit的子类
temp.setMessage(new Apple());//这个是Fruit的子类
temp.setMessage(new Fruit());//这个是Fruit的本身
//Fruit fruit = temp.getMessage(); 不能接收,这里无法确定是哪个父类
System.out.println(temp.getMessage());//只能直接输出
}

通配符的下界只能用来取数据,不能用来存数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值