Java基础----------泛型篇

泛型(Generics)

  1. 在泛型出现之前,我们是怎么解决打印Sting类型和Integer
package org.generics;

/**
 * @author Alex
 * @version 1.0
 */
public class IntegerPrinter {
    Integer content;

    IntegerPrinter(Integer content) {
        this.content = content;
    }

    public void print() {
        System.out.println(content);
    }
}

package org.generics;

/**
 * @author Alex
 * @version 1.0
 */
public class StringPrinter {
    String content;

    StringPrinter(String content) {
        this.content = content;
    }

    public void print() {
        System.out.println(content);
    }
}

package org.generics;

/**
 * @author Alex
 * @version 1.0
 */
public class Main {
    public static void main(String[] args) {
        IntegerPrinter integerPrinter = new IntegerPrinter(123);
        integerPrinter.print();
        StringPrinter stringPrinter = new StringPrinter("你好");
        stringPrinter.print();
    }
}

可见我们想要打印哪种类型的数据,那我们就得创建哪种类型的class。
3. 当引入泛型 Generics 之后

package org.generics;

/**
 * @author Alex
 * @version 1.0
 */
public class Printer<T> {
    T content;

    Printer(T content) {
        this.content = content;
    }

    public void print() {
        System.out.println(content);
    }
}
package org.generics;

/**
 * @author Alex
 * @version 1.0
 */
public class Main {
    public static void main(String[] args) {
        Printer<String> stringPrinter = new Printer<>("你好");
        stringPrinter.print();
        Printer<Integer> integerPrinter = new Printer<>(100);
        integerPrinter.print();
    }
}

声明一个Generics类,我们在类的主体大括号和类名之间的地方去声明,定义一个类型变量名,用尖括号< T >包裹起来就OK了,类型变量名可以是Anything,比如T,K,V
这个时候我们在需要去打印一个String类型的数据时,就不需要在去定义一个StringPrinter的类了。这个时候我们只需要定义一个新的数据类型
需要注意的是: <> 中的类型参数不能是Java 的基本数据类型,比如说< int >,< float >等,我们必须使用一个包装后的一个类型,< String >,< Integer >
5. 在实际项目中,类型参数 T 不需要满足所有的类型,这时我们需要对类型参数进行约束,比如说传入的参数必须是某个类型的子类型也可以是某个类型。< T extends Animal> 代码如下


package org.generics;
/**
 * @author Alex
 * @version 1.0
 * 泛型类
 */
public class Printer<T extends Animal> {
    T content;
    Printer(T content) {
        this.content = content;
    }

    public void print() {
        System.out.println(content);
    }
}

使用这种类型参数必须是Animal的子类的好处是,参数变量T是可以获得Animal方法的,比如说在Animal中定义的方法,可以使用content.方法名() 获取到。
如果说我们不去继承Animal,再去调用Animal中的方法,是报错的,显然是因为Java不知道我们传入的参数类型中是否有这么个方法。

package org.generics;

/**
 * @author Alex
 * @version 1.0
 */
public class Main {
    public static void main(String[] args) {
        // 这里的类型参数就得是Animal 的子类型,就是Dog
        Printer<Dog> stringPrinter = new Printer<>(new Dog());
        stringPrinter.print();
    }
}

这种有约束的操作,在Java中我们称之为有界限的泛型。当然我们也可以使用接口来约束,代码如下,使用接口类型进行约束,也必须使用extends,而不能使用implements。
Animal & Thing 表示必须是 Animal的子类,也必须实现Thing接口

package org.generics;

/**
 * @author Alex
 * @version 1.0
 */
public class Printer<T extends Animal & Thing> {
    T content;
    Printer(T content) {
        this.content = content;
    }

    public void print() {
        System.out.println(content);
    }
}

  1. 其实我们在集合框架中,就使用到了泛型,比如说List、ArrayList、Set等等。
package org.generics;

import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        ArrayList<String> strings = new ArrayList<>();
        strings.add("hello");
        strings.add("hi");
        //其实我们可以通过修改参数类型,可以实现传入任意类型的元素。
        ArrayList<Object> items = new ArrayList<>();
        strings.add("hello");
        strings.add(124);
    }
}

在Java中,不推荐这么做,因为这会带来一个type-safe(类型安全)的问题,举个例子

package org.generics;

import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        ArrayList<Object> items = new ArrayList<>();
        items.add("hello");
        items.add(1234);
        String item1 =(String) items.get(0);
        System.out.println(item1);
        //但是我们获取一下第2个元素
        String item2 = (String) items.get(1);
        //这里打印就会出错了
        System.out.println(item2);
    }
}

报错内容
在这里插入图片描述
可以看出,在编译阶段是没有问题的,但是在运行过程中就会出现问题,以为你想把一个integer类型的数据转成String类型,那怎么可能不报错呢,相当于想把Dog变成Cat。
出现这个问题的原因是:泛型的工作方式是在编译阶段进行类型检查的。而不是运行时。
9. 泛型也经常应用到函数上,称为Generics Method

package org.generics;
import java.util.ArrayList;
public class Main {
    public static void main(String[] args) {
        print("hello");
        print(1424);
        print(12L);
    }
    private static <T> void print(T content){
        System.out.println(content);
    }
}

我们也可以对方法的参数进行约束,跟泛型类的约束是一样的。
10. 最后说一下这个通配符,我们还是通过一个例子引出

package org.generics;
import java.util.ArrayList;
public class Main {
    public static void main(String[] args) {
        ArrayList<String> item1 = new ArrayList<>();
        items.add("hello");
        items.add("nihao");
        print(items);
        //如果我们需要存储Integer类型的元素,并且打印
        ArrayList<Integer> item2 = new ArrayList<>();
        item3.add(12);
        item3.add(22);
        print(item3);
        //这样的话,我就需要改print方法的形参类型,这样是很麻烦的。
        //如果说我们把String改成Object是不是就不用改了。
        //答:这样是不可以的,因为Integer是Object的子类,
        //但是ArrayList<Integer>并不是ArrayList<Object>的子类,因为是一个整体。
        //注意:这里还需要说明一点,泛型是没有继承的。
    }
    private static <T> void print(ArrayList<String> content){
        System.out.println(content);
    }
}

所以我们引出通配符 ?, 这样的话,我们就能

package org.generics;
import java.util.ArrayList;
public class Main {
    public static void main(String[] args) {
        ArrayList<String> item1 = new ArrayList<>();
        items.add("hello");
        items.add("nihao");
        print(items);
        ArrayList<Integer> item2 = new ArrayList<>();
        item3.add(12);
        item3.add(22);
        print(item3);
    }
    private static <T> void print(ArrayList<?> content){
        System.out.println(content);
    }
}

如果说我们不想匹配所有的类型,像前面的泛型类约束是一样的。
11. 还有一种是下界通配符 <? super Dog> 这就要求我们所传入的参数类型必须是Dog的父类,或者是Dog本身。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

^努力努力再努力^

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值