自学内容网 自学内容网

java中的泛型

java中的泛型

  我们先思考一下:为什么需要泛型?

  我们用一个集合进行举例:

public calss Generic {
    public static void main(String[] args) {
        ArrayList arrayList = new ArrayList();
        //添加数据
        arrayList.add(new Dog("大黄"));
        arrayList.add(new Dog("旺财"));
        
        //遍历集合,输出
        for(Object obj: arrayList) {
            Dog dog = (Dog) obj;           
            System.out.println(dog.getName());
        }
        
    }
}

class Dog {
    String name;
    public Dog(String name) {
        this.name = name;
    }
    public String getName() {
        return this.name;
    }
}

  在上面的代码中,我们遍历集合并输出,需要将 Object 类型做一个类型转换,转换成 Dog 类型,这样才可以进行集合中元素的数据输出。但是,我们思考一下就不难发现,如果我们在集合中添加了一个不是 Dog 类型的数据呢?这样一想是不是就觉得,集合的遍历与输出马上就变得复杂了。

总结: 使用传统方式的缺点

  1. 不能对加入到集合的数据类型进行约束(这样的操作是很不安全的)
  2. 遍历的时候,需要进行类型的转换,如果集合中的数据量较大,对效率有一定的影响。

  既然有这个缺点,那我们怎么去解决呢?这就引出了这篇文章的内容 泛型 。没错,使用泛型可以解决以上的问题。

泛型的使用

1 快速入门

public calss Generic {
    public static void main(String[] args) {
        //表示存放到 ArrayList 集合中的元素是Dog类型
        ArrayList<Dog> arrayList = new ArrayList<Dog>();
        //添加数据
        arrayList.add(new Dog("大黄"));
        arrayList.add(new Dog("旺财"));
        //Dog 类型之外的数据无法加入
        //如果编译器发现添加的类型,不满足要求,就会报错 
        //arrayList.add("字符串");报错
        
        //遍历集合,输出
        //在遍历的时候,可以直接取出 Dog 类型而不是 Objec
        for(Dog dog: arrayList) {          
            System.out.println(dog.getName());
        }        
    }
}

class Dog {
    String name;
    public Dog(String name) {
        this.name = name;
    }
    public String getName() {
        return this.name;
    }
}

2 泛型的介绍

(1)使用泛型的好处
  1. 编译时,会检查添加元素的类型,提高了安全性。
  2. 减少了类型转换的次数,提高了效率。
(2)泛型的理解
  1. 泛型又称参数化类型,是 JDK 5.0 出现的新特性,解决数据类型的安全性问题。
  2. 在类声明或实例化时,只要指定好需要的具体类型即可。
  3. Java 泛型可以保证,如果程序在编译时没有发生警告,运行时就不会产生 ClassCastException 异常。同时,代码更加简洁。
  4. 泛型的作用是:可以在类声明时通过一个标识表示类中某个属性的类型,或者是某个方法的返回值的类型,或者是参数类型。
public calss Generic {
    public static void main(String[] args) {
        //相当与 E = String 
        //把 Dog 类中的 E 全部替换成 String
        //就是这条语句的效果
        Dog<String> dog1 = new Dog<String>();
        
        //相当与 E = String 
        //把 Dog 类中的 E 全部替换成 Integer
        //就是这条语句的效果
        Dog<Integer> dog2 = new Dog<Integer>();
        
    }
}

class Dog<E> {
    //属性使用泛型
    E name;
    //构造器使用泛型
    public Dog(E name) {
        this.name = name;
    }
    //方法的返回值使用泛型
    public E getName() {
        return this.name;
    }
}
(3)泛型的语法
  1. 泛型可以在类与接口中使用:

    interface 接口名<T> {}class 类名<K,V> {}

    • 其中,T、K、V 不代表值,而是表示类型。
    • 原则上,任意字母都可以。常用 T 表示,是 Type 的缩写。
  2. 泛型的实例化:要在类名后面指定类型参数的值(类型):

    • List<String> list = new ArrayList<String>();
    • Iterator<Customer> iterator = customers.iterator();
(4)泛型使用的注意事项
  1. interface 接口名<T> {}class 类名<K,V> {}

    • T、K、V 等只能是引用类型,不能是基本数据类型
    • 比如:List<Integer> list = new ArrayList<Integer>(); 可以通过没有问题,Integer 是引用类型。
    • 但是:List<int> list2 = new ArrayList<int>(); 这条语句是错误的,因为 int 为基本数据类型,不是引用类型。
  2. 在给泛型指定具体的类型之后,可以传入该类型或者其子类型。

public class Test {
    public static void main(String[] args) {
        //指定泛型为 A
        ArrayList<A> list = new ArrayList<A>();
        //添加的类型只能是 A 类 及 其子类
        list.add(A);
        //因为 B 类继承了A,所以B类也可以存储在集合 list 中
        list.add(B);
    }
}

class A {}
class B extends A {}
  1. 泛型的使用形式

    • 普通形式:List<Integer> list1 = new ArrayList<Integer>();
    • 简写形式:List<Integer> list2 = new ArrayList<>(); 这种形式下,编译器会根据我们前的内容自动的去推断我们后方的<>中的内容,在实际使用中,我们常用这种形式。
  2. 默认形式下:ArrarList list = new ArrayList(); 在这种形式下,编译器会自动将泛型指定为 Object 类型。


3 自定义泛型

(1)自定义泛型类

基本语法:

//泛型类
class 类名<T, R, ...> {//...表示可以有多个泛型
    // T, R, M 称为泛型的标识符, 一般是单个大写字母
    成员
}
//泛型接口
interface 接口名<T, R, ...> {}

注意事项:

  1. 普通成员可以使用泛型(属性、方法)
  2. 使用泛型的数组,不能初始化
  3. 静态方法中不能使用类的泛型
  4. 泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定类型)
  5. 如果在创建对象时,没有指定类型,默认为 Object。
public class Test {
    public static void main(String[] args) {
        //创建对象不指定泛型,默认为Object
        Student strdent = new Student();
        //没有指定泛型,相当于
        //Student<Object> student = new Student();
    }
}
//泛型类
class Student<T, R> {
    //属性使用泛型
    T t;
    R r;
    //方法使用泛型
    public T getT() {
        return t;
    }
    //数组使用泛型,不能进行初始化
    T[] arr1;
    //错误的语句如下:
    //T[] arr2 = new T[2];
    //因为,泛型是在创建对象时才确定的
    //我们初始化数组,泛型是什么类型还没有确定下来
    //所以我们无法为其分配内存空间
    //java中数据类型所占的字节数是不同的
    //比如 int 4个字节
    //double 8个字节
    
    //静态方法不能使用类的泛型
    /* 下面语句是错误的
    static R f1() {
    return r;
    }
    因为,泛型是在创建对象时指定的
    而静态属性、静态方法,实在类加载进行初始化的
    也就是说,在泛型还没有指定的时候
    我们的静态成员就会先进行初始化
    */
}

(2)自定义泛型接口

基本语法: interface 接口名<T, R, ...> {}

注意事项:

  1. 接口中,静态成员不能使用泛型(和泛型类一样)
  2. 泛型接口的类型,在继承接口或者实现接口时确定
  3. 没有指定泛型类型,默认为Object
public class Test {
    public static void main(String[] args) {
        
    }
}
//泛型接口
interface IA<T, R> {
    
    //下面的语句将会报错
    //T n = 10;
    //因为在接口中,属性都默认为 public static final 修饰
    
    //在普通方法中,可以使用泛型
    default void f1(T t) {      
    }
}
//在继承接口时,指定泛型的类型
interface IB extends IA<String, Integer> {
}
//在实现接口时,指定泛型的类型
class Dog implements IA<Double, String> {}
//如果没有指定,则泛型类型默认为 Object
(3)自定义泛型方法

基本语法:

修饰符 <T, R, ...> 返回类型 方法名(参数列表) {
    
}

注意事项:

  1. 泛型方法,可以定义在普通类中,也可以定义在泛型类中
  2. 当泛型方法被调用时,类型会确定
  3. public void eat(E e) {} ,修饰符后没有 <T, R, …> eat 方法不是泛型方法,而是使用了泛型。
//在普通类中定义泛型方法
class A {
    //泛型方法
    public <T, R> void f1(T t, R r) {}
}
//在泛型类中定义泛型方法
class B<T, R> {
    public <E> void m1() {}
}

public class Test {
    public static void main(String[] arts) {
        //调用泛型方法时,会确定泛型的类型
        A a = new A();
        //泛型方法的,T , R 会根据我们传入的类型自动确定
        //下面的语句,T = Integer; R = String
        //int 会自动进行装箱转为 Integer
        a.f1(10, "xiong");
    }
}

4 泛型的继承和通配符

  1. 泛型不具备继承性

    List<Object> list = new ArrayList<String>();这条语句是错误的,虽然String 是 Object 的子类, 但是泛型不具备继承性。

  2. <?> :支持任意泛型类型。
List<String> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();
//<?> :支持任意泛型类型
test1(list1);
test1(list2);

public void test1(List<?> c) {
     for (Object object : c) { // 通配符,取出时,就是Object
         System.out.println(object);
     }
}
  1. <? extends A> :支持 A 类以及 A 类的子类,规定了泛型的上限。

    ? 表示我们要传入的泛型类型,它可以是 A 类,以及继承了 A 类的子类,不限于直接子类

  2. <? super A> :支持 A 类以及 A 类的父类,不限于直接父类,规定了泛型的下限。

    ? 表示我们要传入的泛型类型,它可以是 A 类,以及 A 类的父类,不限于直接父类


原文地址:https://blog.csdn.net/weixin_74200123/article/details/145311786

免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!