Home Tags Posts tagged with "泛型"

泛型

0 115

今天和猪仔讨论项目的时候遇到一个需求

目前有一个实体类,里面有一些String,int的属性,还有一个List<>类型的属性

但是这个List<>括号里面内容不同而形成不同实体

比如记录日志,该日志分为工作日志和系统日志,那么这两种不同类型的日志的日志时间,日志数量这两个属性对应String、int,是一样的,不同之处在于List<>传入的参数类型不同

一个传入List<Work> 一个传入List<Sys>

我们不想写两个不同的实体类,想用一个实体类来表示这两种不同的日志对象

一开始我的思路是写成 List<Object> 传入,再进行转型

可惜这样无法得到想要的结果,因为List<Object>应该看成一个整体,他不是List<Work>,也不是List<Sys>的父类

因此无法实现向上转型

后来改用 ? 泛型 即可 详情见下文

https://blog.csdn.net/eeeeasy/article/details/80999650?utm_source=app&app_version=4.14.1?utm_source=app

0 105

Java泛型

Java泛型(generics)是JDK5引入的一个新特性,泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型;泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数;

Java的泛型是伪泛型,这是因为Java在编译期间,所有的泛型信息都会被擦掉,这也就是通常所说类型擦除;

一、什么是泛型?

Java泛型设计原则:只要在编译时期没有出现警告,那么运行时期就不会 出现ClassCastException异常;

泛型:把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型;

参数化类型:

把类型当作是参数一样传递

<数据类型>只能是引用类型

相关术语:

ArrayList<E>中的E称为类型参数变量

ArrayList<Integer>中的Integer称为实际类型参数

整个称为ArrayList<E>泛型类型

整个ArrayList<Integer>称为参数化的类型ParameterizedType

 

二、为什么需要泛型

早期Java是使用Object来代表任意类型的,但是向下转型有强制的问题,这样程序就不太安全

首先,如果没有泛型,集合会怎么样呢?

Collection、Map集合对元素的类型是没有任何限制的;本来我的Collection集合装载的是全部的Dog对象,但是外边把Cat对象存储到集合中,是没有任何语法错误的;

把对象扔进集合中,集合是不知道元素的类型是什么的,仅仅知道是Object;因此在get()的时候,返回的是Object;外边获取该对象还需要强制转换

有了泛型之后:

代码更加简洁【不需要强制类型转换】

程序更加健壮【只要在编译时期没有警告,那么运行时期就不会出现ClassCastException异常】

可读性和稳定性【在编写集合的时候,就限定了类型】

2.1 有了泛型后使用增强for遍历集合

在创建集合的时候,我们明确了集合的类型了,所以我们可以使用增强for来遍历集合

运行结果:
1
2
3
三、泛型基础
3.1泛型类
泛型类就是把泛型定义在类上,用户使用该类的时候,才把类型明确下来,此时用户明确了什么类型,该类就代表着什么类型,用户在使用的时候不会担心强转的问题和运行时异常的问题;
3.4类型通配符
需求:方法接收一个集合参数,遍历集合并把元素打印出来,如何做?
按照我们没有学习泛型之前,可能会
public void test(List list){


for(int i=0;i<list.size();i++){

        System.out.println(list.get(i));

    }
}
代码正确,只不过在编译时期会出现警告,说没有确定元素的类型;
学习了泛型之后,可能会:
ublic void test(List<Object> list){


for(int i=0;i<list.size();i++){

        System.out.println(list.get(i));

    }
}
该test()方法只能遍历装载着Object的集合!!!
强调:泛型中的<Object>并不像以前那样有继承关系,也就是说List<Object>和List<String>毫无关系;
我们不清楚List集合装载的元素类型,List<Object>又行不通,于是Java泛型提供了类型通配符?

所以代码应该改成这样:

public void test(List<?> list){


for(int i=0;i<list.size();i++){

        System.out.println(list.get(i));

    }
}

?号通配符表示可以匹配任意类型,任意的Java类都可以匹配…..

现在非常值得注意的是,当我们使用?号通配符的时候:就只能调对象与类型无关的方法,不能调用对象与类型有关的方法。

记住,只能调用与对象无关的方法,不能调用对象与类型有关的方法。因为直到外界使用才知道具体的类型是什么。也就是说,在上面的List集合,我是不能使用add()方法的。因为add()方法是把对象丢进集合中,而现在我是不知道对象的类型是什么。

3.4.1 设定通配符上限

用处:我想接收一个List集合,它只能操作数字类型的元素[Float、Integer、Double、Byte等数字类型都行],怎么做?

我们学习了通配符,但是如果直接使用通配符的话,该集合就不是只能操作数字了。因此我们需要用到设定通配符上限

   List<? extends Number>

上面的代码表示的是:List集合装载的元素只能是Number的子类或自身

作者:Java3y
链接:https://www.zhihu.com/question/272185241/answer/366129174
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

3.4.2设定通配符下限

既然上面我们已经说了如何设定通配符的上限,那么设定通配符的下限也不是陌生的事了。直接来看语法吧

   //传递进来的只能是Type或Type的父类
    <? super Type>

设定通配符的下限这并不少见,在TreeSet集合中就有….我们来看一下

public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
    }

那它有什么用呢??我们来想一下,当我们想要创建一个TreeSet<String>类型的变量的时候,并传入一个可以比较String大小的Comparator。

那么这个Comparator的选择就有很多了,它可以是Comparator<String>,还可以是类型参数是String的父类,比如说Comparator<Objcet>….

这样做,就非常灵活了。也就是说,只要它能够比较字符串大小,就行了

值得注意的是:无论是设定通配符上限还是下限,都是不能操作与对象有关的方法,只要涉及到了通配符,它的类型都是不确定的!

经评论补充:在泛型的上限和下限中有一个原则:PECS(Producer Extends Consumer Super)

书上是这样写的:

  • 带有子类限定的可以从泛型读取【也就是—>(? extend T)】——–>Producer Extends
  • 带有超类限定的可以从泛型写入【也就是—>(? super T)】——–>Consumer Super

也有相关博文写得很好:

3.5通配符和泛型方法

大多时候,我们都可以使用泛型方法来代替通配符的…..

   //使用通配符
public static void test(List<?> list) {

    }

    //使用泛型方法
public <T> void  test2(List<T> t) {

    }

上面这两个方法都是可以的…..那么现在问题来了,我们使用通配符还是使用泛型方法呢??

原则:

  • 如果参数之间的类型有依赖关系,或者返回值是与参数之间有依赖关系的。那么就使用泛型方法
  • 如果没有依赖关系的,就使用通配符,通配符会灵活一些.

3.6泛型擦除

泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据。但编译器编译完带有泛形的java程序后,生成的class文件中将不再带有泛形信息,以此使程序运行效率不受到影响,这个过程称之为“擦除”。

3.6.1兼容性

JDK5提出了泛型这个概念,但是JDK5以前是没有泛型的。也就是泛型是需要兼容JDK5以下的集合的。

当把带有泛型特性的集合赋值给老版本的集合时候,会把泛型给擦除了。

值得注意的是:它保留的就类型参数的上限。

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

        //类型被擦除了,保留的是类型的上限,String的上限就是Object
        List list1 = list;

如果我把没有类型参数的集合赋值给带有类型参数的集合赋值,这又会怎么样??

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

它也不会报错,仅仅是提示“未经检查的转换

四、泛型的应用

如下:

链接:https://www.zhihu.com/question/272185241/answer/366129174
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处