Java泛型

1 泛型简介

Java泛型是Java语言中引入的一种参数化类型机制,它可以在类、接口和方法的定义中使用类型形参(Type Parameter),从而使得代码具有更好的重用性、类型安全性和可读性。

  • 从JDK1.5以后, Java引入了“参数化类型( Parameterized type) ” 的概念,允许我们在创建集合时再指定集合元素的类型, 正如: List, 这表明该List只能保存字符串类型的对象。
  • JDK1.5改写了集合框架中的全部接口和类, 为这些接口、 类增加了泛型支持,从而可以在声明集合变量、 创建集合对象时传入类型实参。
  • 集合没有泛型时:读取出数据需要强制类型转换,有可能抛出ClassCastException
    在这里插入图片描述
  • 集合中使用泛型:可以避免强转,避免抛出ClassCastException
    在这里插入图片描述

List使用泛型:

	@Test
    public void test1() {

        List<String> list = new ArrayList<>();

        list.add("kong");  //因为泛型,只能存String类型

        String str = list.get(0); //泛型的主要作用:避免强制类型转换

        System.out.println(list);
    }

	@Test
    public void test2() {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);

        //迭代器循环
        Iterator<Integer> it = list.iterator();
        while (it.hasNext()) {
            Integer i = it.next();
            System.out.println(i);
        }

        System.out.println("-----------");

        //增强for循环
        for (Integer i : list) {
            System.out.println(i);
        }
    }

Map使用泛型:

	@Test
    public void test1(){
        Map<String,Integer> map = new HashMap<>();
        map.put("1",1);
        map.put("2",1);
        map.put("3",1);

        //entrySet()
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            System.out.println(entry);
        }

        //keySet()
        System.out.println("------------");

        for (String s : map.keySet()) {
            System.out.println(s+"="+map.get(s));
        }

        System.out.println("------------");

        //迭代器
        Iterator<String> it = map.keySet().iterator();
        while (it.hasNext()) {
            String key = it.next();
            System.out.println(key + "=" + map.get(key));
        }
    }

2 自定义泛型

2.1 泛型类

语法:

public class 类名 <泛型类型1,...> {
    ...
}

示例:
Clazz.class

public class Clazz <T1,T2>{

    private T1 t1;
    private T2 t2;

    public T1 getT1() {
        return t1;
    }

    public void setT1(T1 t1) {
        this.t1 = t1;
    }

    public T2 getT2() {
        return t2;
    }

    public void setT2(T2 t2) {
        this.t2 = t2;
    }

    public void f1(T1 t1, T2 t2) {
        System.out.println(t1);
        System.out.println(t2);
    }
}

测试:

public class Test1 {

    public static void main(String[] args) {
        Clazz<String,Integer> clazz = new Clazz<>();
        clazz.f1("kong",18);

        clazz.setT1("xing");
        clazz.setT2(19);

        System.out.println(clazz.getT1());
        System.out.println(clazz.getT2());

        Clazz<String, Clz> bean2 = new Clazz<>();
        bean2.f1("a", new Clz());
    }
}

class Clz {
    @Override
    public String toString() {
        return "Clz{}";
    }
}

结果:

kong
18
xing
19
a
Clz{}

2.2 泛型方法

语法:

public <泛型类型...> 返回类型 方法名(泛型类型 变量名) {
    ...
}

示例:

public class Test1 {
    
    public static <T1, T2> T1 f1(T1 bean, T2 arg) {
        System.out.println(bean);
        return bean;
    }

    public static void main(String[] args) {
        String str = f1("abc", new Date());

        Date date = f1(new Date(), "cba");

        Integer i = f1(123, "aaa");
    }
}

结果:

a
Thu Aug 10 19:42:11 CST 2023
123

2.3 泛型接口

语法:

public interface GenricsInterface<T>{
    ...
}

//实现类
public class Clazz implements GenricsInterface<String> {
    ...
}

示例:
接口GenericsInterface

public interface GenericsInterface<T> {

    public T getValue();

}

实现类IntegerImpl

public class IntegerImpl implements GenericsInterface<Integer> {

    @Override
    public Integer getValue() {
        return 123456;
    }
}

实现类StringImpl

public class StringImpl implements GenericsInterface<String> {

    @Override
    public String getValue() {
        return "hello generics interface";
    }
}

测试

public class Test1 {

    public static void main(String[] args) {
        GenericsInterface<String> g1 = new StringImpl();
        System.out.println(g1.getValue());

        GenericsInterface<Integer> g2 = new IntegerImpl();
        System.out.println(g2.getValue());
    }
}

结果:

hello generics interface
123456

3 泛型通配符

3.1 通配符的上边界

上限通配符(Upper Bounds Wildcards):使用extends关键字来定义上限通配符,表示泛型参数必须是指定类型或其子类。

例如:<? extends T>
在类型参数中使用 extends 表示这个泛型中的参数必须是 T 或者 T 的子类

示例:

public class Test1 {

    public static void f1(List<? extends Father> list) {
    }

    public static void main(String[] args) {
        
        List<GrandFather> grandFatherList = new ArrayList<>();
        List<Father> fatherList = new ArrayList<>();
        List<Mother> motherList = new ArrayList<>();
        List<Son> sonList = new ArrayList<>();
        List<Daughter> daughterList = new ArrayList<>();

        f1(sonList);
        f1(daughterList);
        f1(fatherList);
        //f1(motherList);
    }

    class GrandFather {

    }

    class Father extends GrandFather {

    }

    class Mother {
        
    }

    class Daughter extends Father {

    }

    class Son extends Father {

    }
}

在以上示例中,尝试将 sonList 和 daughterList 传递给 f1 方法,由于它们都是 Father 类型的列表的子类,与 List<? extends Father> 匹配。因此,这些传递是合法的。而motherList不是Father 类型的列表的子类,所以无法传递。

3.2 通配符的下边界

下限通配符(Lower Bounds Wildcards):使用super关键字来定义下限通配符,表示泛型参数必须是指定类型或其父类。

例如:<? super T>
在类型参数中使用 super 表示这个泛型中的参数必须是 T 或者 T 的父类

示例:

public class Test2 {

    public static void f1(List<? super Father> list) {
    }

    public static void main(String[] args) {
        
        List<GrandFather> grandFatherList = new ArrayList<>();
        List<Father> fatherList = new ArrayList<>();
        List<Mother> motherList = new ArrayList<>();
        List<Son> sonList = new ArrayList<>();
        List<Daughter> daughterList = new ArrayList<>();

        f1(fatherList);
        f1(grandFatherList);//? super Father:表示对象边界是Father和Father的父类
        //f1(motherList);

    }

    class GrandFather {

    }

    class Father extends GrandFather {

    }

    class Mother {

    }

    class Daughter extends Father {

    }

    class Son extends Father {

    }
}

在以上示例中,尝试将 fatherList和 grandFatherList传递给 f1 方法,由于它们是 Father 类型或其父类,与 List<? super Father>匹配。因此,这些传递是合法的。而motherList不是Father 类型的父类,所以无法传递。

3.3 泛型边界在接口中的应用

声明接口的泛型只能使用T extends Type

示例:

public class Test1 {
    class Impl1 implements GenricsInterface<Father> {
        @Override
        public Father getValue() {
            return null;
        }
    }
    
    class Impl2 implements GenricsInterface<Mother> {
        @Override
        public Mother getValue() {
            return null;
        }
    }

    class Impl3 implements GenricsInterface<String> {
        @Override
        public String getValue() {
            return null;
        }
    }

    class Impl4 implements GenricsInterface<GrandFather> {
        @Override
        public GrandFather getValue() {
            return null;
        }
    }

    class Impl5 implements GenricsInterface<Son> {
        @Override
        public Son getValue() {
            return null;
        }
    }

    class Impl6 implements GenricsInterface<Daughter> {
        @Override
        public Daughter getValue() {
            return null;
        }
    }

    interface GenricsInterface<T extends Father> {
        public T getValue();
    }

    class GrandFather {

    }

    class Father extends GrandFather {

    }

    class Mother {
        Father father;
    }

    class Daughter extends Father {

    }

    class Son extends Father {

    }
}

在以上示例中提供了一个泛型接口 GenricsInterface,其类型参数 T 必须是 Father 类型或其子类,所以Impl2、Impl3、Impl4都不能实现该接口。

3.4 无界通配符

?表示传入任意类型

示例:

	public static void f2(List<?> list) {

    }

    public void test1() {
        List<GrandFather> grandFatherList = new ArrayList<>();
        List<Father> fatherList = new ArrayList<>();
        List<Mother> motherList = new ArrayList<>();
        List<Son> sonList = new ArrayList<>();
        List<Daughter> daughterList = new ArrayList<>();
        f2(sonList);
        f2(daughterList);
        f2(fatherList);
        f2(grandFatherList);
        f2(motherList);
        f2(new ArrayList<String>());
    }

4 泛型擦除

泛型擦除是Java编程语言中的一种特性,它指的是在编译过程中将泛型类型信息擦除,使得泛型类型在运行时不可见。

Java的泛型是伪泛型,为什么说Java的泛型是伪泛型呢?因为在编译期间,所有的泛型信息都会被擦除掉,我们常称为泛型擦除。

Java中的泛型是通过类型擦除来实现的。在编译过程中,所有的泛型类型参数都会被擦除为它们的上界或者Object 类型。这意味着在运行时,无法获取到泛型类型的具体信息。

示例:

	@Test
    public void test1() {
        List<String> stringList = new ArrayList<String>(); //ArrayList.class
        stringList.add("泛型");
        List<Integer> integerList = new ArrayList<Integer>(); //ArrayList.class
        integerList.add(1);
        System.out.println(stringList.getClass());
        System.out.println(integerList.getClass());
        System.out.println(stringList.getClass() == integerList.getClass()); //true
    }

结果:

class java.util.ArrayList
class java.util.ArrayList
true
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值