Java,泛型

泛型

  • Java的泛型,类似与现实生活中的标签
  • 所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值或参数的类型。这个类型参数将在使用时(例如,继承或实现这个接口、创建对象或调用方法时)确定(即传入实际的类型参数,也称为类型实参)。

在集合和比较器中使用泛型

java 复制代码
	@Test
    public void test1()
    {
        List list = new ArrayList();

        list.add(67);
        list.add(34);
        list.add(78);
        list.add(64);
        list.add(89);
        //问题一类型不安全,因为add的参数是Object类型,意味着任何类型的对象都可以添加成功
//        list.add("AA");//ClassCastException

        Iterator i = list.iterator();
        while (i.hasNext())
        {
            //问题二需要使用强转操作,繁琐还可能导致异常
            Integer integer = (Integer) i.next();
            int score = integer;

            System.out.println(score);
        }
    }


    @Test
    public void test2()
    {
        List<Integer> list = new ArrayList();
        list.add(67);
        list.add(34);
        list.add(78);
        list.add(64);
        list.add(89);
        //编译报错,保证类型安全
//        list.add("aA");
        Iterator<Integer> iterator = list.iterator();
        while (iterator.hasNext())
        {
            //避免了强转操作
            Integer i = iterator.next();
            int score = i;
            System.out.println(i);
        }
    }
    @Test
        public void test3()
        {
    //        HashMap<String,Integer> map = new HashMap<String,Integer>();
            HashMap<String,Integer> map = new HashMap<>();//类型推断
            map.put("Tom",65);
            map.put("Jerry",76);
            map.put("Bob",96);

    //        Set<Map.Entry<String, Integer>> entries = map.entrySet();
    //        Iterator<Map.Entry<String,Integer>> iterator = entries.iterator();

            //jdk10新特性
            var entries = map.entrySet();
            var iterator = entries.iterator();

            while (iterator.hasNext())
            {
                Map.Entry<String,Integer> entry = iterator.next();
                String key = entry.getKey();
                int value = entry.getValue();
                System.out.println(key+"--->"+value);
            }

        }
java 复制代码
//自然排序
public class Employee implements Comparable<Employee>
{
    private String name;
    private int age;
    private MyDate birthday;

    public Employee() {}

    public Employee(String name, int age, MyDate birthday)
    {
        this.name = name;
        this.age = age;
        this.birthday = birthday;
    }
    @Override
    public int compareTo(Employee o)
    {
        //按照姓名排序
        return this.name.compareTo(o.name);
    }
}

//定制排序
@Test
    public void test2()
    {
        Comparator<Employee> comparator = new Comparator<Employee>()
        {
            @Override
            public int compare(Employee o1, Employee o2)
            {
                return o1.getAge()-o2.getAge();
            }
        };

        TreeSet<Employee> set = new TreeSet<>(comparator);
     }
使用说明:
  • 集合框架在声明接口和其实现类时,使用了泛型(jdk5.0),在实例化集合对象时,如果没有使用泛型,默认操作的数据为Object类型的数据;如果使用了泛型,则需要指明泛型的具体类型,一旦指明了泛型的具体类型,则在集合相关方法中,凡是使用类的泛型,都为指定的类型。

泛型的基础说明

1、<类型>这种语法形式就叫泛型。

  • <类型>的形式我们称为类型参数,这里的"类型"习惯上使用T表示,是Type的缩写。即:。
  • :代表未知的数据类型,我们可以指定为,, 等。
    • 类比方法的参数的概念,我们把,称为类型形参,将 称为类型实参,有助于我们理解泛型
  • 这里的T,可以替换成K,V等任意字母。

2、在哪里可以声明类型变量<T>

  • 声明类或接口时,在类名或接口名 后面声明泛型类型,我们把这样的类或接口称为泛型类泛型接口
java 复制代码
【修饰符】 class 类名<类型变量列表> 【extends 父类】 【implements 接口们】{
    
}
【修饰符】 interface 接口名<类型变量列表> 【implements 接口们】{
    
}

//例如:
public class ArrayList<E>    
public interface Map<K,V>{
    ....
}    
  • 声明方法时,在【修饰符】与返回值类型之间声明类型变量,我们把声明了类型变量的方法,称为泛型方法。
java 复制代码
[修饰符] <类型变量列表> 返回值类型 方法名([形参列表])[throws 异常列表]{
    //...
}

//例如:java.util.Arrays类中的
public static <T> List<T> asList(T... a){
    ....
}

自定义泛型类或接口

  • 格式:class A{}

    interface B<T1,T2>{}

  • 当我们在类或接口中定义某个成员时,该成员的相关类型是不确定的,而这个类型需要在使用这个类或接口时才可以确定,那么我们可以使用泛型类、泛型接口。

说明

① 我们在声明完自定义泛型类以后,可以在类的内部(比如:属性、方法、构造器中)使用类的泛型。

② 我们在创建自定义泛型类的对象时,可以指明泛型参数类型。一旦指明,内部凡是使用类的泛型参数的位置,都具体化为指定的类的泛型类型。

③ 如果在创建自定义泛型类的对象时,没有指明泛型参数类型,那么泛型将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。

  • 经验:泛型要使用一路都用。要不用,一路都不要用。

④ 泛型的指定中必须使用引用数据类型。不能使用基本数据类型,此时只能使用包装类替换。

⑤ 除创建泛型类对象外,子类继承泛型类时、实现类实现泛型接口时,也可以确定泛型结构中的泛型参数。

如果我们在给泛型类提供子类时,子类也不确定泛型的类型,则可以继续使用泛型参数。

我们还可以在现有的父类的泛型参数的基础上,新增泛型参数。

注意

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

② JDK7.0 开始,泛型的简化操作:ArrayList flist = new ArrayList<>();

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

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

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

⑤ 在类/接口上声明的泛型,在本类或本接口中即代表某种类型,但不可以在静态方法中使用类的泛型。

⑥ 异常类不能是带泛型的。

自定义泛型方法

  • 泛型方法,不是使用了泛型参数的方法
  • 格式:
    • 权限修饰符 返回值类型 方法名 (形参列表){//通常在形参列表或返回值类型的位置会出现泛型参数}
    • 举例:public E method(E e) {};
  • 说明:
    • 声明泛型方法时,指明其泛型类型
java 复制代码
//自定义泛型方法
public <E> E method(E e)
{
    return null;
}

//定义泛型方法,将E[]数组元素添加到对应类型的ArrayList中,并返回
public <E> ArrayList<E> copyFromArrayToList(E[] arr)
{
    ArrayList<E> list = new ArrayList<>();
    for (E e : arr)
    {
        list.add(e);
    }
    return list;
}

泛型在继承上的体现

  • 类SuperA是类A的父类,则G与G是并列的两个类,没有任何子父类的关系。
    • 比如:ArrayList、ArrayList没有关系
  • 类SuperA是类的父类或接口,SuperA 与A 的关系:SuperA 与A 有继承或实现的关系。即A 的实例可以赋值给SuperA 类型的应用(或变量)
    • 比如:List与ArrayList

通配符的使用

  • ?的使用
    • 以集合为例:可以读取数据、不能写入数据(例外null)
  • ? extends A(<=)
    • 以集合为例:可以读取数据、不能写入数据(例外null)
  • ? super A (>)
    • 以集合为例:可以读取数据、可以写入A类型的数据或A类型子类的数据(例外null)
相关推荐
Vallelonga2 小时前
浅谈 Rust bindgen 工具
开发语言·rust
ElfBoard2 小时前
ElfBoard技术贴|如何在ELF-RK3506开发板上构建AI编程环境
c语言·开发语言·单片机·嵌入式硬件·智能路由器·ai编程·嵌入式开发
Qiuner2 小时前
Windows安装Dokcer Desktop与汉化
windows·docker·架构
木木木一2 小时前
Rust学习记录--C13 Part1 闭包和迭代器
开发语言·学习·rust
木木木一2 小时前
Rust学习记录--C13 Part2 闭包和迭代器
开发语言·学习·rust
Wcy30765190662 小时前
文件包含漏洞及PHP伪协议
开发语言·php
海南java第二人2 小时前
SpringBoot循环依赖全解:从根源到解决方案的深度剖析
java·spring
CopyProfessor2 小时前
Java Agent 入门项目模板(含代码 + 配置 + 说明)
java·开发语言
枫叶丹42 小时前
【Qt开发】Qt系统(八)-> Qt UDP Socket
c语言·开发语言·c++·qt·udp