Java SE面向对象--13.Java泛型

版权声明:转载请注明原始链接 https://blog.csdn.net/sswqzx/article/details/82849893

1、概述

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

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

程序在运行时发生了问题java.lang.ClassCastException。怎么解决这个问题 呢、下面就来说说泛型

泛型可以在类或方法中预支地使用未知的类型。用来灵活地将数据类型应用到不同的类、方法、接口当中。将数据类型作为参数进行传递。

好处:

  • 将运行时期的ClassCastException,转移到了编译时期变成了编译失败。

  • 避免了类型强转的麻烦。

2、定义泛型类

格式:

修饰符 class 类名<代表泛型的变量> {  }
// 自定义一个泛型类, 泛型的标志是 <>
// E (Element)  T (Type)  K (Key)  V (Value)  R (Result)

// 请问 : 泛型类中的泛型在何时进行确定 ??? 在实例化该对象时, 进行泛型真实类型的确定.
// 泛型类中泛型是为本类的属性和方法提供的.  ArrayList<E>
public class GenericClass1<T> {

    // 属性
    private T field;

    // 方法 :
    public void setField(T field) {
        this.field = field;
    }

    public T getField() {
        return field;
    }
}

演示代码:

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

        // 1. 创建一个泛型类对象
        GenericClass1<String> g1 = new GenericClass1<>();

        g1.setField("hello Java.");
        String str = g1.getField();
        System.out.println(str);


        GenericClass1<Integer> g2 = new GenericClass1<>();

        g2.setField(998);
        Integer number = g2.getField();
        System.out.println(number);
    }
}

3、定义泛型接口

格式:

修饰符 interface接口名<代表泛型的变量> {  }
// 定义了一个泛型接口 : 类可以在实例化同时确定类上的泛型, 而接口不能实例化, 何时确定泛型呢 ??? 实现类来确定.

public interface MyGenericInterface<T> {

    // 抽象方法
    void method(T t);
}

演示代码:

接口代码一:

// 确定接口中泛型的第一种方式, 实现类在实现该接口的同时进行确定.

public class MyGenericInterfaceImpl1 implements MyGenericInterface<String> {

    @Override
    public void method(String s) {
   
        System.out.println(s);
    }
}

接口代码二:

// 确定接口中泛型的第二种方式, 当实现类实现该接口时, 不确定该接口的泛型, 此时, 该类必须定义为 `泛型类`.
// 泛型类在实例化对象时, 就可以确定泛型的真实类型, 此时, 该类型就可以被传递接口的泛型中, 从而来确定接口的泛型.

public class MyGenericInterfaceImpl2<T> implements MyGenericInterface<T> {

    @Override
    public void method(T t) {
        System.out.println(t);
    }
}

测试类:

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

        // 1. 实现类 MyGenericInterfaceImpl1
        MyGenericInterfaceImpl1 g1 = new MyGenericInterfaceImpl1();
        g1.method("你好, 上海.");

        // ArrayList<E> 实现了 List<E> 接口. 

        // 2. 实现类 MyGenericInterfaceImpl2
        MyGenericInterfaceImpl2<String> g2 = new MyGenericInterfaceImpl2<>();
        g2.method("hello ShangHai.");

        MyGenericInterfaceImpl2<Integer> g3 = new MyGenericInterfaceImpl2<>();
        g3.method(998);
    }
}

4、定义泛型方法

格式:

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

代码:

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

        printInfo(new Person("张三", 18));

        printInfo(new Animal("哮天犬", 38));
    }

    // 定义一个方法 : 输出 `对象信息`  (Person, Animal)  -> Object
    // 泛型必须先定义, 后使用.  泛型使用 <> 尖括号定义.  位置: 修饰符 <T> 返回值类型 方法名(参数列表) { 方法体 }
    public static <T> void printInfo(T t) {
        System.out.println(t);
    }
}

5、泛型通配符:?

说明:当使用泛型类或者接口时,传递的数据中,泛型类型不确定,可以通过通配符<?>表示。但是一旦使用泛型的通配符后,只能使用Object类中的共性方法,集合中元素自身方法无法使用。

使用场景 : 必须在已经定义的接口或类上的泛型位置才能使用.

演示:

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

        ArrayList<Person> list1 = new ArrayList<>();
        list1.add(new Person("张三", 18));
        list1.add(new Person("李四", 19));
        list1.add(new Person("王五", 20));

        printCollection(list1);


        ArrayList<Animal> list2 = new ArrayList<>();
        list2.add(new Animal("哮天犬1号", 100));
        list2.add(new Animal("哮天犬2号", 200));

        printCollection(list2);
    }

    // 需求 : 定义一个方法, 输出集合中所有元素的信息.
    // ? 泛型的通配符, 好处, 不要声明.
    // 默认 ? extends Object
    // 翻译 : ? 表示我不知道这是什么类型, 但是, 我知道, 它可以是 Object 类型, 或者该类型 继承 Object 类型.
    public static void printCollection(Collection<?> c) {
        for (Object obj : c){
            System.out.println(obj);
        }
    }

    /*public static <T> void printCollection(Collection<T> c) {
        for (T t : c) {
            System.out.println(t);
        }
    }*/
}

泛型上下限:

泛型的上限:
格式: 类型名称 <? extends 类 > 对象名称
意义: 只能接收该类型及其子类
泛型的下限:
格式: 类型名称 <? super 类 > 对象名称
意义: 只能接收该类型及其父类型

上限:

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

        ArrayList<Person> list1 = new ArrayList<>();
        printCollection(list1);

        ArrayList<Student> list2 = new ArrayList<>();
        printCollection(list2);

        ArrayList<Teacher> list3 = new ArrayList<>();
        printCollection(list3);

        // ArrayList<Animal> list4 = new ArrayList<>();
        // printCollection(list4);
    }

    // 定义一个方法 :
    public static void printCollection(Collection<? extends Person> c) {
        for (Person p : c) {
            System.out.println(p);
        }
    }
}

下限:

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

        ArrayList<ArmyDog> list1 = new ArrayList<>();
        printCollection(list1);

        ArrayList<Dog> list2 = new ArrayList<>();
        printCollection(list2);

        ArrayList<Animal> list3 = new ArrayList<>();
        printCollection(list3);

        /*
        ArrayList<Cat> list4 = new ArrayList<>();
        printCollection(list4);

        ArrayList<Wolf> list5 = new ArrayList<>();
        printCollection(list5);
        */
    }

    // 方法 :
    public static void printCollection(Collection<? super ArmyDog> c) {
        for (Object obj : c) {
            System.out.println(obj);
        }
    }
}

总结:

注意 : 如果泛型不指定, 那么该泛型就会被提升为 Object 类型.

好处1 : 统一了集合中元素类型.
好处2 : 避免了类型强转问题. 
好处3 : 将运行时期的类型转换异常, 提前到编译时期进行语法检查.

猜你喜欢

转载自blog.csdn.net/sswqzx/article/details/82849893