问题背景

Java 集合有个缺点,把一个对象"丢进"集合里之后,集合就会"忘记"这个对象的数据类型,当再次取出该对象时 该对象的编译类型就变Object类型(其运行时类型没变),Java集合之所以被设计成这样,是因为集合的设计者不知道我们会用集合来保存什么类型的对象所以他们把集合设计成能保存任何类型的对象,只要求具有很好的通用性。但是,有可能使用过程中不符合预期,导致运行时报错。
import java.util.ArrayList;
import java.util.List; public class ProblemTest {
public static void main(String[] args) {
List persons = new ArrayList();
persons.add("李一桐");
persons.add("刘亦菲");
persons.add("鞠婧祎");
//不小心加入了一个int类型的数据
persons.add(10);
persons.forEach(person ->
System.out.println(((String) person).length())); //报错int转换不成String报错
}
}

解决:

  • 为要存储的元素/变量 灵活指定数据类型

1、泛型使用:

  • 作用:<泛型>指定集合存储元素的类型
语法:
  • 集合类<元素类型..> = new 集合类<>;
Set<String> persons = new HashSet<>();
Map<String, Integer> persons = new HashMap<>();

2、自定义泛型

语法:

    自定义类名<泛型形参>{ ... }

Iterator对象定义泛型:

Map定义泛型:

我们就是在类名的后面加上类型的替代符,比如上面的T,K,V。我们在使用的时候,就直接把这些T,K,V替换成对应的类型名称就好了。

/**
* @ClassName MyDefineClassInitTypeExample
* @projectName: object1
* @author: Zhangmingda
* @description: 自定义泛型测试
* date: 2021/4/11.
*/
public class MyDefineClassInitTypeExample {
private static class Student<T>{
private T type; public Student(T type) {
this.type = type;
} public T getType() {
return type;
}
}
public static void main(String[] args) {
Student<String> student = new Student<>("高中");
System.out.println(student.getType());//高中
Student<Integer> student1 = new Student<>(202101);
System.out.println(student1.getType());//202101
}
}

3、从泛型类派生子类

语法:

  自定义子类名<子类泛型形参> extends 父类名<泛型形参> {

    子类构造方法(数据类型|子类泛型形参 形参){

           super(形参)

               }

  }

  

当创建了带泛型申明的接口或父类之后,可以为该接口创建实现类或创建该类派生子类。子类必须指定父类的泛型具体类型,或者子类必须实现和父类相同的泛型
比如下面代码是不行的:

    private static class  ElementaryStudent extends Student<T>{
public ElementaryStudent(T type){
super(type);
}
}
但是我们可以指定具体父类泛型派生一个子类:

public class MyDefineClassInitTypeExample {
private static class Student<T>{
private T type; public Student(T type) {
this.type = type;
} public T getType() {
return type;
}
}
private static class ElementaryStudent extends Student<String>{
public ElementaryStudent(String type){
super(type);
}
} public static void main(String[] args) { ElementaryStudent elementaryStudent = new ElementaryStudent("小学");
System.out.println(elementaryStudent.getType()); //小学
}
}

定义一个子类的时候,无法指定父类泛型的具体类型:则

/**
* @ClassName MyDefineClassInitTypeExample
* @projectName: object1
* @author: Zhangmingda
* @description: 自定义泛型测试
* date: 2021/4/11.
*/
public class MyDefineClassInitTypeExample {
private static class Student<T>{
private T type; public Student(T type) {
this.type = type;
} public T getType() {
return type;
}
}
private static class ElementaryStudent<T> extends Student<T>{
public ElementaryStudent(T type){
super(type);
}
} public static void main(String[] args) { ElementaryStudent<Integer> elementaryStudent1 = new ElementaryStudent<>(202107);
System.out.println(elementaryStudent1.getType());
}
}
不存在泛型类:
  • 用一个支持泛型的类,创建不同类型的实例,他们还是同一个类的实现。
        ElementaryStudent<Integer> elementaryStudent1 = new ElementaryStudent<>(202107);
System.out.println(elementaryStudent1.getType());
ElementaryStudent<String> elementaryStudent2 = new ElementaryStudent<>("幼儿园");
System.out.println(elementaryStudent2.getType());
System.out.println(elementaryStudent1.getClass()); //class MyDefineClassInitTypeExample$ElementaryStudent
System.out.println(elementaryStudent2.getClass()); //class MyDefineClassInitTypeExample$ElementaryStudent
System.out.println(elementaryStudent.getClass().equals(elementaryStudent2.getClass()));//true

4、 泛型通配符?

问题:List<Object> 和 List<String> 什么关系,要求传入List<Object> 传入List<String> 行不行?

import java.util.ArrayList;
import java.util.List; public class ProblemTest {
private static void test(List<Object> list) {
for (Object o : list) {
System.out.println(o);
}
}
public static void main(String[] args) {
List<String> persons = new ArrayList<>();
persons.add("李 ");
persons.add("刘 ");
persons.add("祎");
test(persons); //不行,他们是同一类不同的实现,不是子类关系
}
}

解决:用通配符?替代泛型<Object>

import java.util.ArrayList;
import java.util.List; /**
* @ClassName wildcardExample
* @projectName: object1
* @author: Zhangmingda
* @description: XXX
* date: 2021/4/11.
*/
public class WildcardExample {
private static void test(List<?> list){
list.forEach(o -> System.out.println(o));
}
public static void main(String[] args) {
List<String> list = new ArrayList();
list.add("张三");
list.add("李四");
test(list);
/**
* 张三
* 李四
*/
}
}
上面的这个List,其中的?表示的就是一个通配符,表示可以匹配任何类型。在用的时候,通配符里面的元素,都是Object类型。

5、限定通配符适配范围

5.1 在List集合使用中限定示例代码:继承Animal类的子类都匹配

语法:

  <?extends 类名>

本例如果直接指定List<Animal>泛型,Dog和Cat虽然是Animal的子类,但是List<Dog> List<Cat> 和<List Animal>之间并不是子类关系,List<Dog> 或List<Cat>不能匹配<List Animal>。

import java.util.ArrayList;
import java.util.List; /**
* @ClassName wildcardExample
* @projectName: object1
* @author: Zhangmingda
* @description: XXX
* date: 2021/4/11.
*/
public class WildcardLimitExample {
private static class Animal{
public void say(){
System.out.println("动物叫了..........");
}
}
private static class Dog extends Animal{
@Override
public void say() {
System.out.println("I am Dog : 汪汪汪");
}
}
private static class Cat extends Animal{
@Override
public void say() {
System.out.println("I am Cat : 喵喵喵!!!");
}
}
public static void animalSay(List<? extends Animal> animals){
animals.forEach(animal -> animal.say());
}
public static void main(String[] args) {
List<Animal> animals = new ArrayList<>();
animals.add(new Dog());
animals.add(new Cat());
animalSay(animals);
/**
* I am Dog : 汪汪汪
* I am Cat : 喵喵喵!!!
*/
System.out.println("==================="); List<Dog> dogs = new ArrayList<>();
dogs.add(new Dog());
animalSay(dogs);
List<Cat> cats = new ArrayList<>();
cats.add(new Cat());
animalSay(cats);
/**
* I am Dog : 汪汪汪
* I am Cat : 喵喵喵!!!
*/
}
}

5.2在类的定义过程中给泛型限定范围

语法:

 修饰符... class<T extends 父类名 [& 接口名] >

  • 形参T代表数据类型 extends 后面为T要继承或实现的接口

 如下类class CustomerNumber<T extends Number & Serializable> 的意义为:

在用该类创建对象时候如果指定泛型,指定传入构造方法的数据类型T必须是 继承了Number类并且实现了Serializable接口的数据类型。

import java.io.Serializable;
import java.text.NumberFormat; /**
* @ClassName WildcardLimitCustomerNumberExample
* @projectName: object1
* @author: Zhangmingda
* @description: XXX
* date: 2021/4/11.
*/
public class WildcardLimitCustomerNumberExample {
private static class CustomerNumber<T extends Number & Serializable>{
private T info; public CustomerNumber(T info) {
this.info = info;
} public T getInfo() {
return info;
}
} public static void main(String[] args) {
CustomerNumber<Integer> customerNumber = new CustomerNumber<>(1);
System.out.println(customerNumber.getInfo());
}
}

java数据类型:集合存储元素类型限制<泛型> ;自定义类指定泛型<T> 以及限制用法;派生子类泛型<T> super(泛型内参数); 泛型通配符?以及?限制用法的更多相关文章

  1. HashSet集合存储数据的结构和HashSet集合存储元素不重复的原理

    HashSet集合存储数据的结构 HashSet集合存储元素不重复的原理 //创建HashSet集合对象 Hashset<String> set = new HashSet<> ...

  2. JDBC数据类型、Java数据类型、标准sql类型

    本概述是从<JDBCTM Database Access from JavaTM: A Tutorial and Annotated Reference>这本书中摘引来的.JavaSoft ...

  3. JAVA数据类型中的char类型

    1.JAVA中,char占2字节,16位.可在存放汉字 2.char赋值 char a='a'; //任意单个字符,加单引号. char a='中';//任意单个中文字,加单引号. char a=11 ...

  4. 阶段1 语言基础+高级_1-3-Java语言高级_04-集合_06 Set集合_4_Set集合存储元素不重复的原理

    set集合元素为什么不能重复 集合重写了toString的方法所以打印是里面的内容 往里面存了三次abc 哈希表,初始容量是16个 set集合存储字符串的时候比较特殊 横着是数组,竖着就是链表结构.跟 ...

  5. 向集合中添加自定义类型--建议在自定义类型的时候要重写equals方法

    package com.bjpowernode.t01list; import java.util.ArrayList; /* * 向集合中添加自定义类型 */public class TestLis ...

  6. Java中集合删除元素时候关于ConcurrentModificationException的迷惑点

    下面的示例来至于阿里巴巴Java开发手册的集合处理部分的第7条: 运行如下代码,会发现正确运行. public static void hasNotExcption() { List<Strin ...

  7. Java操作Redis存储对象类型数据

    背景描述      关于JAVA去操作Redis时,如何存储一个对象的数据,大家是非常关心的问题,虽然官方提供了存储String,List,Set等等类型,但并不满足我们现在实际应用.存储一个对象是是 ...

  8. java按照集合中元素的属性进行排序示例代码

    public class Student { private String name; private int age; private int id; public Student() {  sup ...

  9. Java 数据类型:集合接口Collection之Set接口HashSet类;LinkedHashSet;TreeSet 类

    Collection 之 Set 实现类: HashSet TreeSet 特点: 无序. 元素不可重复. (如果试图添加一个已经有的元素到一个Set集合中,那么会添失败,add()方法返回false ...

随机推荐

  1. redis实现最简单的锁

    <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifact ...

  2. 贪心/构造/DP 杂题选做Ⅲ

    颓!颓!颓!(bushi 前传: 贪心/构造/DP 杂题选做 贪心/构造/DP 杂题选做Ⅱ 51. CF758E Broken Tree 讲个笑话,这道题是 11.3 模拟赛的 T2,模拟赛里那道题的 ...

  3. 洛谷 P7163 - [COCI2020-2021#2] Svjetlo(树形 dp)

    洛谷题面传送门 神仙级别的树形 dp. u1s1 这种代码很短但巨难理解的题简直是我的梦魇 首先这种题目一看就非常可以 DP 的样子,但直接一维状态的 DP 显然无法表示所有情况.注意到对于这类统计一 ...

  4. Atcoder Grand Contest 005 E - Sugigma: The Showdown(思维题)

    洛谷题面传送门 & Atcoder 题面传送门 记先手移动棋子的树为红树,后手移动棋子的树为蓝树. 首先考虑一个性质,就是如果与当前红色棋子所在的点相连的边中存在一条边,满足这条边的两个端点在 ...

  5. Atcoder Grand Contest 034 F - RNG and XOR(FWT)

    Atcoder 题面传送门 & 洛谷题面传送门 tsc 考试前 A 的题了,结果到现在才写这篇题解--为了 2mol 我已经一周没碰键盘了,现在 2mol 结束算是可以短暂的春天 短暂地卷一会 ...

  6. [Linux]非root的R环境被conda破坏后如何恢复?

    记录说明 这篇文章本来是用来记录Linux非root环境下安装PMCMRplus包折腾过程,但后来试过了各种方法安装不上这个R包后,我换上了Miniconda来安装.经前人提醒,一开始安装Minico ...

  7. [R] 如何绘制各样本的pathway丰度热图?

    前言 一般而言,我们做完pathway富集分析,就做下气泡图或bar图来进行展示,但它们实际上只考虑了富集因子和Pvalue.如果我们不关注这两个因素,而是在乎样本本身的pathway丰度呢? 对于K ...

  8. Oracle--计算某一日期为一年中的第几周

    我自己实现的脚本: select T31267.CREATED_DATE as F31265, (select to_char(to_date(T31267.CREATED_DATE,'yyyy-mm ...

  9. JAVA中数组的基本概念与用法

    JAVA中数组的基本概念与用法 1. 数组的定义与特点 数组的一种引用数据类型 数组中可以同时存放多个数据,但是数据的类型必须统一 数组的长度在开始时就需要确定,在程序运行期间是不可改变的 虽然可以使 ...

  10. 学习java第十九天

    一.今日收获 1.java完全学习手册第三章算法的3.2排序,比较了跟c语言排序上的不同 2.观看哔哩哔哩上的教学视频 二.今日问题 1.快速排序法的运行调试多次 2.哔哩哔哩教学视频的一些术语不太理 ...