泛型方法的定义和使用_泛型( Generic )

3136efeb5098419b23c36676621fc4c7.png

泛型(Generic)

1. 泛型概述

  • 泛型是一个未知的, 不确定的数据类型. 比如ArrayList 中的E, 就是一个未知的不确定的数据类型, 那么他就是一个泛型
  • 泛型虽然是一个未知的, 不确定的数据类型, 但他不是一直未知, 一直不确定当我们使用这个类的时候, 会指定这个泛型的类型. 比如ArrayList , ArrayList
  • 泛型可以省略, 如果泛型省略, 相当于泛型是Object. 如ArrayLIst list = new ArrayList();这样的话就可以放任何类型的数据.
  • 泛型也是语法糖, 下面会用例子介绍
package drafts.drafts10;

import java.util.ArrayList;

public class GenericTest {
    public static void main(String[] args) {

        //不适用泛型
        ArrayList list = new ArrayList();
        //list.add(100);编译时不报错, 运行时报错
        list.add("hello");
        list.add("world");
        list.add("java");
        for (Object obj : list) {
            String str = (String) obj;
            System.out.println(str.length());
        }

        //使用泛型
        ArrayList<String> list2 = new ArrayList<>();
        //list2.add(100);编译时期报错
        list2.add("hello");
        list2.add("world");
        list2.add("java");
        for (String str : list2) {
            System.out.println(str.length());
        }
    }
}

前面曾提过, 集合中是可以存放任意对象的, 只要把对象储存集合后, 那么这是他们都会被提升成Object类型. 当我们在取出每一个对象, 并进行相应的操作, 必须==采用类型转换==.

看一段代码:

import java.util.Iterator;

public class GenericTest {
    public static void main(String[] args) {
        Collection coll = new ArrayList();
        coll.add("捣乱黄");
        coll.add("捣乱绿");
        coll.add(5);//由于集合并没有限定,所以任何类型都可以给其中存放
        Iterator it = coll.iterator();
        while (it.hasNext()){
            //需要打印每个字符串的长度, 就要把迭代出来的对象转成String类型
            String str =(String) it.next();
            System.out.println(str.length());
        }
    }
}

//3
//3
//Exception in thread "main" java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String (java.lang.Integer and java.lang.String are in module java.base of loader 'bootstrap')
//  at drafts.drafts3.GenericTest.main(GenericTest.java:16)

程序在运行时抛出了java.lang.ClassCastException.类型转换异常

为什么会出现此异常呢?

由于集合中什么类型都可以储存, 导致取出时强制转换, 引发运行时ClassCastException.

Collection虽然可以储存各种对象, 但实际上, 通常Collection只储存同一类型对象, 因此, 在JDK1.5后, 新增了泛型(Generic)语法

使得在设计API时可以指定类或方法支持泛型, 这样我们使用API的时候也变得更为简洁, 并得到了==编译时运行检查==.

  • 泛型 : 可以在类或方法中预支的使用未知的类型,
tips : 一般在创建对象时, 将未知的类型确定具体的类型, 当没有指定泛型时, 默认类型为Object类型.

2. 泛型的好处

  1. 将运行时期的ClassCastException, 转移到了编译时期变成编译失败
  2. 省去了向下转型的操作(只是操作层面省去了, 下详)

看下面一段代码: 体现了省去向下转型

package drafts.drafts4;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class GenericTest2 {
    public static void main(String[] args) {
        Collection <String> list = new ArrayList<>();
        list.add("捣乱黄");
        list.add("捣乱绿");
        //list.add(666); 这时, 不是String类型的元素就会编译报错
        Iterator<String> it = list.iterator();
        while (it.hasNext()){
            String str = it.next();
            //当使用Iterator<String>控制元素类型后, 就不需要强转了, 
            //获取到的元素就是String类型
            System.out.println(str.length());
        }
    }
}
tips: 泛型时数据类型的一部分, 我们将类名与泛型合并一起看做数据类型

泛型擦除: 泛型也是语法糖, 本质还是要做向下转型.

Java中的泛型都是伪泛型, 泛型只在源代码阶段有效, 一旦编译, 泛型就会消失, 俗称泛型参数

package drafts.drafts10;
import java.util.ArrayList;
/*
    泛型擦除, 在用xjad反编译.class文件后发现
    Java中的泛型都是伪泛型, 泛型只在源代码阶段有效, 一旦编译, 泛型就会消失, 俗称泛型擦除
 */
@SuppressWarnings("all")
public class GenericTest2 {
    public static void main(String[] args) {
        ArrayList<String> list2 = new ArrayList<>();
        //list2.add(100);编译时期报错
        list2.add("hello");
        list2.add("world");
        list2.add("java");
        for (String str : list2) {
            System.out.println(str.length());
        }       
        //编译之后泛型就没了, 本质其实还是向下转型

    }
}

3. 泛型的定义与使用

​ 泛型, 是一种未知的不确定的数据类型. 用来灵活的将数据类型应用到不同的类, 方法, 接口中. 将数据类型作为参数进行传递.

​ 如果定义类的时候, 在类名后面加上, 此时就表示定义了一个未知的, 不确定的数据类型T , 这个T就是一个泛型, T可以用任何字母代替, 但一般用T (type)

这个位置的数据类型T会在我们使用这个类的时候确定下来

### 3.1 含有泛型的类( 泛型类 )

定义格式 :

修饰符 class 类名 <代表泛型的变量> {    }

例如, API中的ArrayList集合

class ArrayList<E>{
    public boolean add (E e) {}
    public E get (int index) {}
    ...
}

在类中定义的泛型在整个类中都可以使用

泛型在定义的时候不具体, 使用的时候才具体, 在使用的时候确定泛型的具体数据类型

在创建对象的时候确定泛型 :

例如, ArrayList<String> list = new ArrayList<String> ();

此时, 变量E的值就是String类型. 那么我们的类型就可以理解为 :

class ArrayList<String>{
    public boolean add (String e) {}
    public String get (int index) {}
    ...
}

3.2 含有泛型的方法

​ 如果想要延后泛型类型的确认时间, 那么可以使用泛型方法. 如果将泛型定义在方法上, 那么该方法就是一个泛型方法

​ 在方法上定义的泛型, 需要调用方法的时候才能够确定这个泛型是什么类型

定义格式 :

修饰符 <代表泛型的变量> 返回值类型 方法名 (参数列表) { ... }

例如 :

public class MyGenericMethod {
    public <E> void show <E e> {
        System.out.println(mvp.getClass());
    }

    public <E> E show2 (E e) {
        return e;
    }
}

调用方法时, 确定泛型的类型

public class GenericMethodDemo {
    public static void main (String[] args){
        //创建对象
        MyGenericMethod mm = new MyGenericMethod();

        mm.show("aaa");    //class java.lang.String
        mm.show(1234);     //class java.lang.Integer
        mm.show(12.211);   //class java.lang.Double

    }
}

定义方法, 接收什么参数, 就返回什么结果

: 表示在方法上定义了一个不确定的数据类型E

后面两个E: 使用泛型当做参数和返回值的数据类型

在方法上定义的泛型, 可以在整个方法中使用

在方法上定义的泛型, 需要调用这个方法的时候才能确定这个泛型表示的是什么类型.

public <E> E getSame(E e) {
    return e ;
}

小结 :

如果在类上面定义泛型, 那么类就是泛型类, 在类上面定义的泛型, 可以在整个类中使用, 类上面定义的泛型需要使用整个类的时候才能确定泛型的类型

如果在方法上面定义泛型, 那么这个方法就是泛型方法, 在方法上面定义的泛型, 可以在整个方法中使用, 方法上面定义的泛型需要使用该方法的时候才能确定泛型的类型

3.3 含有泛型的接口

如果在定义接口的时候在接口名后面加上尖括号, 那么这个接口就是一个泛型接口, 在接口中定义的泛型, 在整个接口中都可以使用.

定义格式 :

修饰符 interface 接口名<代表泛型的变量> {...}

例如 :

public interface MyInterface <T> {//表示定义了一个不确定的数据类型T
    public abstract void add (E e);

    public abstract void E getE();
}

使用格式 :

3.3.1 定义类的时候确定泛型的类型

例如

public class MyImp1 implements MyGenericInterface<String> {
    @Override
    public void add(String e) {
        // 省略...
    }

    @Override
    public String getE() {
        return null;
    }
}

此时, 泛型E 的值就是String类型

3.3.2 直到创建对象时, 才确定泛型的类型

例如

public class MyImp2<E> implements MyGenericInterface<E> {

    @Override
    public void add(E e) {
        // 省略...
    }

    @Override
    public E getE() {
        return null;
    }
}

确定泛型 :

/*
* 使用
*/
public class GenericInterface {
    public static void main(String[] args) {
        MyImp2<String> my = new MyImp2<String>();            
        my.add("aa");
    }
}

泛型接口的使用:

3.4 泛型通配符

泛型之间是没有继承关系的, 比如ArrayList并不是ArrayList的父类
如果想要让泛型可以匹配任何类型的数据, 那么可以使用泛型通配符.
我们用?表示泛型通配符.
==注意 :== 泛型通配符要使用在参数位置==被动匹配==, 不能主动使用.
3.4.1 泛型通配符的使用
import java.util.ArrayList; /* 泛型之间是没有继承关系的。比如ArrayList<Object>并不是ArrayList<String>的父类。 如果想要让泛型可以匹配任何类型的数据,那么可以使用泛型通配符。 ? 表示泛型通配符,可以匹配任何类型的泛型。 注意: 泛型通配符要使用在参数位置被动匹配, 不能主动使用。 */ public class Demo01Generic { public static void main(String[] args) { //创建集合,用来保存字符串 ArrayList<String> strList = new ArrayList<>(); //添加元素 strList.add("Hello"); strList.add("World"); strList.add("Java"); //调用printArrayList方法,遍历集合 printArrayList(strList); //创建集合,保存Integer ArrayList<Integer> intList = new ArrayList<>(); //调用printArrayList,遍历 printArrayList(intList); //创建集合,使用?当做泛型类型 //ArrayList<?> list = new ArrayList<>(); //list.add(); } /* 定义一个方法,用来遍历存储任何类型数据的集合。 参数: ArrayList<Object> */ public static void printArrayList(ArrayList<?> list) { //?表示泛型通配符,可以匹配任何类型的泛型。 //对参数list集合进行遍历 for (Object obj : list) { System.out.println(obj); } } }
3.4.2 泛型限定
如果想要对?泛型通配符的使用范围进行限制,那么可以使用泛型限定(上限,下限)
<? extends A>:泛型类型要么是A类,要么是A类的子类, 孙子类...。 上限。 ​ <? super A>: 泛型类型要么是A类,要么是A类的父类, 爷爷类 直到Object类。 下限。
​ 泛型主要用于代码的重构.
示例:
import java.util.ArrayList; /* 如果想要对?泛型通配符的使用范围进行限制,那么可以使用泛型限定(上限,下限) <? extends A>:泛型类型要么是A类,要么是A类的子类。 上限。 <? super A>: 泛型类型要么是A类,要么是A类的父类。 下限。 泛型主要用于代码的重构. */ public class Demo02Generic { public static void main(String[] args) { //创建集合 ArrayList<Student> stuList = new ArrayList<>(); //添加元素 stuList.add(new Student("jack", 20)); stuList.add(new Student("rose", 12)); stuList.add(new Student("tony", 24)); //调用printArrayList方法 printArrayList(stuList); //创建集合 ArrayList<Person> personList = new ArrayList<>(); printArrayList(personList); //创建集合 ArrayList<Object> objList = new ArrayList<>(); //printArrayList(objList); 要求泛型要么是Person,要么是Person的子类,不能是Person的父类 //method(stuList); 泛型类型要么是Person,要么是Person的父类,不能是Person的子类 method(personList); method(objList); } /* 定义方法,使用集合当做参数。 */ public static void method(ArrayList<? super Person> list) { //泛型类型要么是Person,要么是Person的父类 } /* 要求:定义方法,遍历保存Person或者Person子类对象的集合。 */ public static void printArrayList(ArrayList<? extends Person> list) {//泛型的类型要么是Person,要么是Person的子类 for (Person p : list) { System.out.println(p); } } }

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值