Java知识梳理之泛型用法详解

目录
  • 泛型
    • 作用
    • 集合中泛型
    • 自定义泛型
    • 通配符
    • 2.注意点
    • 3.有限制的通配符

泛型

背景:

从JDK 5.0以后,Java引入了“参数化类型(Parameterized type)”的概念,允许我们在创建集合时再指定集合元素的类型,正如:List ,这表明该List只能保存字符串类型的对象。

作用

解决元素存储的安全性问题,好比商品、药品标签,不会弄错。

解决获取数据元素时,需要类型强制转换的问题,好比不用每回拿商品、药品都要辨别。

@Test
public void test1(){
    ArrayList list = new ArrayList();
    //需求:存放学生的成绩
    list.add(78);
    list.add(76);
    list.add(89);
    list.add(88);
    //问题一:类型不安全
    //        list.add("Tom");

    for(Object score : list){
        //问题二:强转时,可能出现ClassCastException
        int stuScore = (Integer) score;

        System.out.println(stuScore);
    }
}

//在集合中使用泛型,以ArrayList为例
@Test
public void test1(){
    ArrayList<String> list = new ArrayList<>();
    list.add("AAA");
    list.add("BBB");
    list.add("FFF");
    list.add("EEE");
    list.add("CCC");
    //遍历方式一:
    Iterator<String> iterator = list.iterator();
    while (iterator.hasNext()){
        System.out.println(iterator.next());
    }
    System.out.println("-------------");
    //便利方式二:
    for (String str:
         list) {
        System.out.println(str);
    }
}

集合中泛型

① 集合接口或集合类在JDK 5.0时都修改为带泛型的结构。

② 在实例化集合类时,可以指明具体的泛型类型

③ 指明完以后,在集合类或接口中凡是定义类或接口时,内部结构(比如:方法、构造器、属性等)使用到类的泛型的位置,都指定为实例化的泛型类型。

比如:add(E e) —>实例化以后:add(Integer e)

④ 注意点:泛型的类型必须是类,不能是基本数据类型。需要用到基本数据类型的位置,拿包装类替换

⑤ 如果实例化时,没有指明泛型的类型。默认类型为 java.lang.Object 类型

说明

1.泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如<E1,E2,E3>

2.泛型类的构造器如下: public GenericClass(){}

而下面是错误的: public GenericClass<E>{}

3.实例化后,操作原来泛型位置的结构必须与指定的泛型类型一致。

4.泛型不同的引用不能相互赋值。

尽管在编译时 ArrayList和ArrayList是两种类型,但是,在运行时只有一个ArrayList被加载到JVM中。

5.泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。

建议:泛型要使用一路都用。要不用,一路都不要用。

6.如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象。

7.JDK 7.0,泛型的简化操作: ArrayList<Fruit>first= new ArrayList<>();(类型推断)

8.泛型的指定中不能使用基本数据类型,可以使用包装类替换。

9.在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法中不能使用类的泛型。

10.异常类不能是泛型的。

11.不能使用 new E[]。但是可以:E[] elements= (E[])new Object[capacity];

参考:ArrayList源码中声明:Object[] elementData,而非泛型参数类型数组。

12.父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型

子类不保留父类的泛型:按需实现

  • 没有类型—擦除
  • 具体类型

子类保留父类的泛型:泛型子类

  • 全部保留
  • 部分保留

结论:子类必须是“富二代”,子类除了指定或保留父类的泛型,还可以增加自己的泛型

自定义泛型

泛型类、泛型接口、泛型方法

泛型的声明

interface List<T> 和 class GenTest<K,V> 其中,T,K,V,不代表值,而是表示类型。这里使用任意字母都可以。

常用T表示,是Type的缩写。

泛型的实例化

一定要在类名后面指定类型参数的值(类型)。如:

List<String> strList =new ArrayList<String>();
Iterator<Customer> iterator = customers.iterator();

T只能是类,不能用基本数据类型填充。但可以使用包装类填充

把一个集合中的内容限制为一个特定的数据类型,这就是 generics背后的核心思想

//JDK 5.0以前
Comparable c = new Date();
System.out.println(c.comparaTo("red");

//JDK 5.0以后
Comparable <Date> c = new Date();
System.out.println(c.comparaTo("red");

自定义泛型类

代码示例:

/**
 * 自定义泛型类Order
 */
class Order<T> {
    private String orderName;
    private int orderId;
    //使用T类型定义变量
    private T orderT;

    public Order() {
    }
    //使用T类型定义构造器
    public Order(String orderName, int orderId, T orderT) {
        this.orderName = orderName;
        this.orderId = orderId;
        this.orderT = orderT;
    }

    //这个不是泛型方法
    public T getOrderT() {
        return orderT;
    }
    //这个不是泛型方法
    public void setOrderT(T orderT) {
        this.orderT = orderT;
    }
    //这个不是泛型方法
    @Override
    public String toString() {
        return "Order{" +
                "orderName='" + orderName + '\'' +
                ", orderId=" + orderId +
                ", orderT=" + orderT +
                '}';
    }
//    //静态方法中不能使用类的泛型。
//    public static void show(T orderT){
//        System.out.println(orderT);
//    }

//    //try-catch中不能是泛型的。
//    public void show(){
//        try {
//
//        }catch (T t){
//
//        }
//    }

    //泛型方法:在方法中出现了泛型的结构,泛型参数与类的泛型参数没有任何关系。
    //换句话说,泛型方法所属的类是不是泛型类都没有关系。
    //泛型方法,可以声明为静态的。
    // 原因:泛型参数是在调用方法时确定的。并非在实例化类时确定。
    public static <E> List<E> copyFromArryToList(E[] arr) {
        ArrayList<E> list = new ArrayList<>();
        for (E e :
                list) {
            list.add(e);
        }
        return list;
    }
}

自定义泛型接口

代码示例:

/**
 * 自定义泛型接口
 */
public interface DemoInterface <T> {
    void show();
    int size();
}

//实现泛型接口
public class Demo implements DemoInterface {
    @Override
    public void show() {
        System.out.println("hello");
    }

    @Override
    public int size() {
        return 0;
    }
}

@Test
//测试泛型接口
public void test3(){
    Demo demo = new Demo();
    demo.show();
}

自定义泛型方法

方法,也可以被泛型化,不管此时定义在其中的类是不是泛型类。在泛型方法中可以定义泛型参数,此时,参数的类型就是传入数据的类型。

泛型方法的格式: [访问权限]<泛型>返回类型 方法名(泛型标识 参数名称])抛出的异常

泛型方法声明泛型时也可以指定上限

泛型方法声明泛型时也可以指定上限

代码示例:

//泛型方法:在方法中出现了泛型的结构,泛型参数与类的泛型参数没有任何关系。
//换句话说,泛型方法所属的类是不是泛型类都没有关系。
//泛型方法,可以声明为静态的。
// 原因:泛型参数是在调用方法时确定的。并非在实例化类时确定。
public static <E> List<E> copyFromArryToList(E[] arr) {
    ArrayList<E> list = new ArrayList<>();
    for (E e :
         list) {
        list.add(e);
    }
    return list;
}

通配符

1.通配符的使用

使用类型通配符:?

比如:List<?>,Map<?,?>

List<?> 是 List<String>、List<Object> 等各种泛型 List 的父类。

读取 List<?> 的对象list中的元素时,永远是安全的,因为不管list的真实类型是什么,它包含的都是Object

写入list中的元素时,不可以。因为我们不知道c的元素类型,我们不能向其中添加对象。 除了添加null之外。

说明:

将任意元素加入到其中不是类型安全的

Collection<?> c = new ArrayList<String>()

c.add(new Object());//编译时错误

因为我们不知道c的元素类型,我们不能向其中添加对象。add 方法有类型参数 E 作为集合的元素类型。我们传给add的任何参数都必须是一个已知类型的子类。因为我们不知道那是什么类型,所以我们无法传任何东西进去。

唯一的例外的是 null,它是所有类型的成员。

我们可以调用 get() 方法并使用其返回值。返回值是一个未知的类型,但是我们知道,它总是一个Object。

代码示例:

@Test
public void test3(){
    List<Object> list1 = null;
    List<String> list2 = null;

    List<?> list = null;

    list = list1;
    list = list2;
    //编译通过
    //        print(list1);
    //        print(list2);

    //
    List<String> list3 = new ArrayList<>();
    list3.add("AA");
    list3.add("BB");
    list3.add("CC");
    list = list3;
    //添加(写入):对于List<?>就不能向其内部添加数据。
    //除了添加null之外。
    //        list.add("DD");
    //        list.add('?');

    list.add(null);

    //获取(读取):允许读取数据,读取的数据类型为Object。
    Object o = list.get(0);
    System.out.println(o);
}

public void print(List<?> list){
    Iterator<?> iterator = list.iterator();
    while(iterator.hasNext()){
        Object obj = iterator.next();
        System.out.println(obj);
    }
}

2.注意点

//注意点1:编译错误:不能用在泛型方法声明上,返回值类型前面<>不能使用?
public static <?> void test(ArrayList<?> list){

}

//注意点2:编译错误:不能用在泛型类的声明上
class GenericTypeClass<?>{

}

//注意点3:编译错误:不能用在创建对象上,右边属于创建集合对象
ArrayList<> list2 new ArrayList<?>();

3.有限制的通配符

1.<?>:允许所有泛型的引用调用

2.通配符指定上限

上限 extends:使用时指定的类型必须是继承某个类,或者实现某个接口,即 <=

3.通配符指定下限

下限 super:使用时指定的类型不能小于操作的类,即 >=

4.举例:

<?extends Number>(无穷小, Number\]

只允许泛型为Number及Number子类的引用调用

<?super Number>\[Number,无穷大)

只允许泛型为Number及Number父类的引用调用

<? extends Comparable>

只允许泛型为实现 Comparable接口的实现类的引用调用

代码示例:

@Test
public void test4(){

    List<? extends Person> list1 = null;
    List<? super Person> list2 = null;

    List<Student> list3 = new ArrayList<Student>();
    List<Person> list4 = new ArrayList<Person>();
    List<Object> list5 = new ArrayList<Object>();

    list1 = list3;
    list1 = list4;
    //        list1 = list5;

    //        list2 = list3;
    list2 = list4;
    list2 = list5;

    //读取数据:
    list1 = list3;
    Person p = list1.get(0);
    //编译不通过
    //Student s = list1.get(0);

    list2 = list4;
    Object obj = list2.get(0);
    编译不通过
    //        Person obj = list2.get(0);

    //写入数据:
    //编译不通过
    //        list1.add(new Student());

    //编译通过
    list2.add(new Person());
    list2.add(new Student());

}

到此这篇关于Java知识梳理之泛型用法详解的文章就介绍到这了,更多相关Java泛型内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java详细分析讲解泛型

    目录 1.泛型概念 2.泛型的使用 2.1泛型类语法 2.2泛型方法语法 2.3泛型接口语法 2.4泛型在main方法中的使用 3.擦除机制 4.泛型的上界 5.通配符 5.1通配符的上界 5.2通配符的下界 6.包装类 6.1装箱和拆箱 1.泛型概念 泛型就是将类型参数化 所谓类型参数化就是将类型定义成参数的形式,然后在使用此类型的时候的时候再传入具体的类型 到这我们可以看出来:泛型在定义的时候是不知道具体类型的,需要在使用的时候传入具体的类型,泛型可以用在类.接口和方法中,这样做的好处是一个

  • 一篇文章带你入门java泛型

    目录 一.什么是泛型 二.语法 三.示例 1.简单示例 2.返回最大值-支持各种数据类型 3.泛型类 4.类型通配符 总结 一.什么是泛型 Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型. 泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数. 二.语法 你可以写一个泛型方法,该方法在调用时可以接收不同类型的参数.根据传递给泛型方法的参数类型,编译器适当地处理每一个方法调用. 下面是定

  • Java通俗易懂讲解泛型

    目录 1.什么是泛型 2.引出泛型 3.泛型类的语法 4.裸类型 5.泛型如何编译的 5.1 擦除机制 5.2.泛型数组为什么不能实例化 6.泛型的上界 7.通配符 7.1.通配符能用来干嘛 7.2.通配符的上界(读数据) 7.3.通配符的下界(写数据) 1.什么是泛型 泛型是在JDK1.5引入的新的语法,通俗讲,泛型:就是适用于许多许多类型.从代码上讲,就是对类型实现了参数化. 看到标红的这句话,有些兄弟可能会有疑惑,我们以前传参,传得是一个整形数据,传得是一个引用,我从来没传过类型嘛,类型难

  • 深入浅出理解Java泛型的使用

    目录 一.泛型的意义 二.泛型的使用 三.自定义泛型类 1.关于自定义泛型类.泛型接口: 2.泛型在继承方面的体现 3.通配符的使用 一.泛型的意义 二.泛型的使用 1.jdk 5.0新增特性 2.在集合中使用泛型: 总结: A.集合接口或集合类在jdk5.0时都修改为带泛型的结构. B.在实例化集合类时,可以指明具体的泛型类型. C.指明完以后,在集合类或接口中凡是定义类或接口时,内部结构(比如:方法.构造器.属性等)使用类的泛型的位置, 都指定为实例化的泛型类型.比如:add(E e) --

  • java简明例举讲解泛型

    目录 什么是泛型 泛型类与接口派生子类 泛型通配符 类型擦除 什么是泛型 早期的Object类型可以接收任意的对象类型,但是在实际的使用中, 会有类型转换的问题.也就存在这隐患,所以Java提供了泛型来解决这个安全问题 泛型,即“参数化类型”.一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传 递实参.就是说 , 我们可以将类型向参数一样传递过去 //一个泛型类 //T可以为任意字符,如A,a,B等都可以 public class Demo1<T> { private T num;

  • Java 泛型超详细入门讲解

    目录 1.什么是泛型? 2.泛型是怎么编译的 泛型的编译机制:擦除机制 1.什么是泛型? 泛型其实就是将类型作为参数传递,泛型允许程序员在编写代码时使用一些以后才指定的类型 ,在实例化该类时将想要的类型作为参数传递,来指明这些类型. 为什么要引入泛型? 例如:自己实现一个顺序表 public class MyArrayList { public int[] elem; public int usedSize; public MyArrayList() { this.elem = new int[

  • Java知识梳理之泛型用法详解

    目录 泛型 作用 集合中泛型 自定义泛型 通配符 2.注意点 3.有限制的通配符 泛型 背景: 从JDK 5.0以后,Java引入了“参数化类型(Parameterized type)”的概念,允许我们在创建集合时再指定集合元素的类型,正如:List ,这表明该List只能保存字符串类型的对象. 作用 解决元素存储的安全性问题,好比商品.药品标签,不会弄错. 解决获取数据元素时,需要类型强制转换的问题,好比不用每回拿商品.药品都要辨别. @Test public void test1(){ Ar

  • Java map.getOrDefault()方法的用法详解

    Map.getOrDefault(Object key, V defaultValue)方法的作用是: 当Map集合中有这个key时,就使用这个key值: 如果没有就使用默认值defaultValue. 代码示例如下: HashMap<String, String> map = new HashMap<>(); map.put("name", "cookie"); map.put("age", "18"

  • 浅谈java Iterator.remove()方法的用法(详解)

    实例如下: @Test public void tt(){ List<String> list = new ArrayList<String>(); list.add( "0" ); list.add( "1" ); list.add( "2" ); list.add( "3" ); list.add( "4" ); list.add( "5" ); list.a

  • Java Quartz触发器CronTriggerBean配置用法详解

    CronTrigger表达式分为七项子表达式,其中每一项以空格隔开,从左到右分别是:秒,分,时,月的某天,月,星期的某天,年:其中年不是必须的,也就是说任何一个表达式最少需要六项!  例:0 0 12 ? * WED 表示每个星期三的12点执行,这里没有"年"这项!  字段名(项)  必须  值范围  特殊字符  秒 是 0-59  , - * /   分 是 0-59  , - * /   时 是 0-23  , - * /   月的某天  是 1-31  , - * ? / L W

  • Java之Pattern.compile函数用法详解

    除了Pattern Pattern.compile(String regex), Pattern类的compile()方法还有另一个版本: Pattern Pattern.complie(String regex,int flag),它接受一个标记参数flag,以调整匹配的行为. flag来自以下Pattern类中的常量: 编译标记 效果 Pattern.CANON_EQ 两个字符当且仅当它们的完全规范分解相匹配时,就认为它们是匹配的,例如,如果我们指定这个标记,表达式a\u030A就会匹配字符

  • Java中ByteArrayInputStream和ByteArrayOutputStream用法详解

    目录 ByteArrayInputStream ByteArrayOutputStream ByteArrayInputStream 介绍ByteArrayInputStream 是字节数组输入流.它继承于 InputStream. InputStream 通过read()向外提供接口,供它们来读取字节数据:而 ByteArrayInputStream 的内部额外的定义了一个计数器,它被用来跟踪 read() 方法要读取的下一个字节. 它包含一个内部缓冲区,该缓冲区包含从流中读取的字节.也就是说

  • Java集合类之TreeSet的用法详解

    目录 上节回顾 TreeSet集合概述和特点 构造方法 方法摘要 Demo 自然排序Comparable的使用 比较器排序Comparator的使用 上节回顾 LinkedHashSet集合概述及特点 LinkedHashSet集合特点 哈希表和链表实现Set接口,具有可预测的迭代次序 由链表保证元素有序,也就是说元素的存储和取出顺序是一致的 由哈希表保证元素唯一,也就是说没有重复元素 LinkedHashSet集合的储存和遍历: import java.util.LinkedHashSet;

  • Java构造函数与普通函数用法详解

    函数也被称为方法! 函数的作用及特点: 1.用于定义功能,将功能封装. 2.可以提高代码的复用性. 函数注意事项: 1.不能进行函数套用(不可以在函数内定义函数). 2.函数只有被调用才能被执行. 3.基本数据类型(String.int.-.)修饰的函数类型,要有return返回值. 4.void修饰的函数,函数中的return语句可以省略不写. 5.函数名可以根据需求进行命名. 代码示例:(有无函数/方法的区别) 无函数/方法代码例子: public class NoFunc { public

  • Java switch关键字原理及用法详解

    这篇文章主要介绍了Java中 switch关键原理及用法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Switch语法 switch作为Java内置关键字,却在项目中真正使用的比较少.关于switch,还是有那么一些奥秘的. 要什么switch,我有if-else 确实,项目中使用switch比较少的一个主要原因就在于它的作用能被if-else代替,况且switch对类型的限制,也阻碍了switch的进一步使用. 先看看switch的语法

  • Java汉字转拼音pinyin4j用法详解

    一.工具介绍 pinyin4j 是一个支持将简体和繁体中文转换到成拼音的Java开源类库: 1. 功能 支持同一汉字有多个发音 还支持拼音的格式化输出,比如第几声之类的, 同时支持简体中文.繁体中文转换为拼音-使用起来也非常简单.下面是其官方网址,其中提供了下载: pinyin4j的官方下载地址 2. 目录结构及说明 doc : pinyin4j的api文档 lib : pinyin4j的jar包 src: pinyin4j的源代码 CHANGELOG.txt : pinyin4j的版本更新日志

随机推荐