Java进阶 - [1-5] 泛型

 

一、什么是泛型

  早期Java是使用Object来代表任意类型的,但是向下转型有强转的问题,这样程序并不安全

  针对List、Set、Map等集合类型,它们对存储的元素类型是没有任何限制的。例如向List中存储Dog类型的对象,但是有人把Cat对象也存储到这个List中了,那么在编译上是没有任何语法错误的。

  所有使用该泛型参数的地方都被统一化,保证类型一致。如何未指定具体类型,默认是Object类型。集合体系中的所有类都增加了泛型,泛型也主要用于集合

 

 

二、泛型类

  泛型类就是把泛型定义在类class ClassName<T>上,用户使用该类的时候,才把类型明确下来。这样的话,用户明确了什么类型,该类就代表着什么类型,用户在使用的时候就不用担心强转的问题,和运行时转换异常的问题了。

package com.harley.genericity;

import lombok.Data;

/**
 * @author harley
 * @since 2024-03-20 17:28:39
 */
public class ClassGeneric {

    public static void main(String[] args) {

        ObjectUtil<String> stringUtil = new ObjectUtil<>();
        stringUtil.setObj("Harley");
        System.out.println(stringUtil.getObj());

        ObjectUtil<Integer> integerObj = new ObjectUtil<>();
        integerObj.setObj(11);
        System.out.println(integerObj.getObj());

    }
    
    @Data
    static class ObjectUtil<T>{
        private T obj;
    }
}

编译报错

 

 

三、泛型方法

  除了在类上使用泛型,我们可能就仅仅在某个方法上需要使用泛型,外界仅仅是关心该方法,不关心类其他的属性,这样的话,我们在整个类上定义泛型,未免就有些大题小做了。那么此时,我们可以采用泛型方法。调用方法,输入的参数是什么类型,T就是什么类型。

package com.harley.genericity;

/**
 * @author harley
 * @since 2024-03-20 17:48:47
 */
public class MethodGenericity {

    public static void main(String[] args) {
        ObjectTool tool = new ObjectTool();
        tool.show("Hello");
        tool.show(21);
        tool.show(true);
    }
    static class ObjectTool{
        public <T> void show(T t){
            System.out.println(t);
        }
    }
}

 

 

四、泛型类派生出的子类

  泛型类是拥有泛型这个特性的类,本质还是一个Java类,可以被继承(泛型类)或实现(泛型接口)。这里分为两种情况。

首先创建一个Inter接口,在该接口上使用泛型

interface Inter<T>{
    void show(T t);
}

1、子类明确泛型类的类型参数变量

class InterImpl1 implements Inter<String>{
    @Override
    public void show(String s){
        System.out.println(s);
    }
}

2、子类不明确泛型类的的类型参数变量

class InterImpl2<T> implements Inter<T>{
    @Override
    public void show(T t){
        System.out.println(t);
    }
}

3、完整的代码

package com.harley.genericity;

/**
 * @author harley
 * @since 2024-03-20 18:06:46
 */
public class SubClassGenericity {

    public static void main(String[] args) {

        Inter<String> i = new InterImpl1();
        i.show("你好");

        Inter<Integer> i2 = new InterImpl2();
        i2.show(1);

    }

}

interface Inter<T>{
    void show(T t);
}

class InterImpl1 implements Inter<String>{

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

class InterImpl2<T> implements Inter<T>{

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

 

 

五、类型通配符

  List<T> 表示元素类型未知的List,它可以匹配任何类型的元素。声明List<?> list后,不能向集合中添加元素,因为无法确定集合的元素类型,唯一例外的是null。

1、List<T>的使用

package com.harley.genericity;

import java.util.ArrayList;
import java.util.List;

/**
 * @author harley
 * @since 2024-03-20 18:15:00
 */
public class TypeWildcard1 {

    public static void main(String[] args) {

        List<String> list = new ArrayList<>();
        list.add("路飞");
        list.add("索隆");
        list.add("娜美");
        print1(list);

        List<Integer> list2 = new ArrayList<>();
        list2.add(30000000);
        list2.add(150000000);
        list2.add(300000000);
        print2(list2);

    }

    public static void print1(List<String> list){
        for (String str :list) System.out.println(str);
    }

    public static void print2(List<Integer> list){
        for (Integer i: list) System.out.println(i);
    }
}

2、List<?>的使用

package com.harley.genericity;

import java.util.ArrayList;
import java.util.List;

/**
 * @author harley
 * @since 2024-03-20 18:22:21
 */
public class TypeWildcard2 {

    public static void main(String[] args) {

        List<String> list1 = new ArrayList<>();
        list1.add("路飞");
        list1.add("索隆");
        list1.add("娜美");
        print1(list1);

        List<Integer> list2 = new ArrayList<>();
        list2.add(30000000);
        list2.add(150000000);
        list2.add(300000000);
        print1(list2);
    }
    
    public static void print1(List<?> list){
        for (Object obj:list) System.out.println(obj);
    }
}

3、List<?>的特点

因为无法确定集合的元素类型,所以不能向集合中添加元素,会报编译错误

唯一例外的是可以添加null

 

4、如何解决3中不能添加元素的问题

public static <T> void print1(List<T> list){
    list.add(list.get(0));
    list.add(null);
    for (T t:list) System.out.println(t);
}

 

 

六、泛型的上限和下限

1、泛型的上限

  • 格式:类型名称 <? extends 类> 对象名称
  • 意义:只能接收该类型及其子类
package com.harley.genericity;

import java.util.ArrayList;
import java.util.List;

/**
 * @author harley
 * @since 2024-03-20 18:45:08
 * 指定类型通配符的上限 —— 返回值为类型的方法可以使用了
 */
public class TypeWildcard4 {
    public static void main(String[] args) {
        List<Integer> intList = new ArrayList<>();
        List<Long> longList = new ArrayList<>();
        print(intList);
        print(longList);
    }

    public static void print(List<? extends Number> list){
        /*
         因为调用本方法时传递的实参可能是List<Long>,也可能是List<Integer>
         list.add(new Integer(100));
         list.add(new Long(100));
        */
        list.add(null);
        
        // 可以调用get()方法返回集合中的元素
        for (int i = 0; i < list.size(); i++) {
            Number obj = list.get(i);
            System.out.println(obj);
        }
    }
}

 

2、泛型的下限

  • 格式:类型名称 <? super 类> 对象名称
  • 意义:只能接收该类型及其父类型
package com.harley.genericity;

import java.util.ArrayList;
import java.util.List;

/**
 * @author harley
 * @since 2024-03-20 18:53:20
 */
public class TypeWildcard5 {

    public static void main(String[] args) {
        List<Integer> intList = new ArrayList<>();
        List<Number> numList = new ArrayList<>();
        intList.add(1);
        numList.add(2);
        print(intList);
        print(numList);
    }

    /* list中可以放的类型为Integer及其父类Number类型 */
    public static void print(List<? super Integer> list){
        list.add(new Integer(100));
        list.add(null);
        for (int i = 0; i < list.size(); i++) {
            Object obj = list.get(i);
            System.out.println(obj);
        }
    }
}

 

 

七、类型擦除

  泛型是提供给javac编译器使用的,它可以作为类型的限制,让编译器在源代码级别上,挡住非法类型的数据。

  但是在JDK1.5之前没有泛型的概念,为了能够与之前版本代码兼容,编译器编译完带有泛型的java程序后,生成的class字节码文件中将不再带有泛型信息,这个过程称之为“擦除”。

 

“擦除”前的代码

public interface Animal<T>{
    void eat(T t);
}

public class Cat implements Animal<String>{
    @Override
    public void eat(String s){
        System.out.println("cat eat" + s);
    }
}

“擦除”后的代码

public interface Animal{
    void eat(Object t);
}

public class Cat implements Animal{
    @Override
    public void eat(String s){
        System.out.println("cat eat" + s);
    }
}

 

八、桥接方法的作用

  桥接方法是jdk1.5引入泛型后,为使java泛型方法生成的字节码与jdk1.5版本之前的字节码兼容由编译器自动生成的。可用method.isBridge()判断method是否是桥接方法。

 

自动生成的桥接方法

public interface Animal{
    void eat(Object t);
}

public class Cat implements Animal{
    @Override
    public void eat(Object s){
        this.eat((String) s)
    }
    
    public void eat(String s){
        System.out.println("cat eat" + s)
    }
    
}

 

 

 

实例演示

1、创建一个Animal的接口

package com.harley.bridgemethod;

/**
 * @author harley
 * @since 2024-03-20 19:13:40
 */
public interface Animal<T> {
    void eat(T t);
}

2、创建一个cat类实现Animal接口,并重写方法

package com.harley.bridgemethod;

/**
 * @author harley
 * @since 2024-03-20 19:13:35
 */
public class Cat implements Animal<String>{
    @Override
    public void eat(String s) {
        System.out.println("cat eat " + s);
    }
}

3、创建一个测试类,父类引用指向子类对象,并调用子类的eat方法

package com.harley.bridgemethod;

/**
 * @author harley
 * @since 2024-03-20 19:13:05
 */
public class BridgeMethodTest {
    public static void main(String[] args) {
        Animal<String> animal = new Cat();
        animal.eat("fish");
    }
}

4、使用泛型,解析方法类型

package com.harley.bridgemethod;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * @author harley
 * @since 2024-03-20 19:15:08
 */
public class MethodFactory {
    public static void main(String[] args) {
        Method[] methods = Cat.class.getDeclaredMethods();
        for (Method method : methods) {
            // 获取方法名称
            String name = method.getName();
            Class<?>[] parameterTypes = method.getParameterTypes();
            System.out.println(name + "("+ Arrays.toString(parameterTypes));
        }
    }

}

执行结果证明了会自动生成桥接方法

 

 

 

 

— 要养成终生学习的习惯 —

posted @ 2024-03-20 17:37  HOUHUILIN  阅读(4)  评论(0编辑  收藏  举报