0
点赞
收藏
分享

微信扫一扫

Java基础-泛型-泛型语法与使用

Java工程师知识树 / Java基础


泛型介绍

Java泛型是JDK1.5中引入的一个新特性,其本质是参数化类型,也就是说所操作的数据类型被指定为一个参数(ParameterizedType),这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法

泛型概念:

把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型

相关术语:

  • ArrayList<E>中的E称为类型参数变量
  • ArrayList<Integer>中的Integer称为实际类型参数
  • 整个称为ArrayList<E>泛型类型
  • 整个ArrayList<Integer>称为参数化的类型ParameterizedType

泛型的好处:

  • 第一是泛化:代码更加简洁【不用强制转换】。早期Java是使用Object来代表任意类型的,但是向下转型有强转的问题,这样程序就不太安全。
  • 第二是类型安全:程序更加健壮【只要编译时期没有警告,那么运行时期就不会出现ClassCastException异常】。传入类型不正确时,编译不通过。
  • 第三是消除强制类型转换:可读性和稳定性【在编写集合的时候,就限定了类型】。

泛型语法

泛型类型命名约定

在定义泛型时,用于指定泛型的变量是一个大写字母。其实任意一个大写字母都可以,代表的意义也是是完全相同的,但为了提高可读性,也是需要统一的泛型类型的命名约定的。

命名约定可以帮助我们轻松地理解代码,拥有命名约定是Java编程语言的最佳实践之一。 通常,类型参数名称(标识符)是单个大写字母,以使其易于与Java变量区分开

最常用的类型参数名称为:

  • E :元素(Element,常用在java Collection里。如:List<E>,Iterator<E>,Set<E>)
  • K :键(Key,Value中的Key代表Map的键 Map<K,V>)
  • V :值(Key,Value中的Value代表Map的值 Map<K,V>)
  • N :数字(Number)
  • T :类型(Type,Java 类 TypeParam<T>)
  • S,U等 :第二,第三类型

通配符

通配符:传入的类型有一个指定的范围,从而可以进行一些特定的操作

?:表示不确定的java类型(无限制通配符类型)。

泛型中有三种通配符形式:

  1. <?> :无限制通配符类型
  2. <? extends E> extends :关键字声明了类型的上界,表示参数化的类型可能是所指定的类型,或者是此类型的子类。
  3. <? super E> super :关键字声明了类型的下界,表示参数化类型可能是指定类型,或者是此类型的父类。

Object与?通配符的区别:

Object是所有类的根类,任何类的对象都可以设置给该Object引用变量,但是使用的时候可能需要类型强制转换,而用使用了泛型T、E等这些标识符后,在实际用的时候会将参数类型指定不需要再进行类型强制转换

通配符?与字母泛型变量类型E,T等的区别:

  • 泛型变量T不能在代码用于创建变量,只能在类,接口,函数中声明以后,才能使用。
  • 无边界通配符?则只能用于填充变量类型,表示通配任何类型!只是填充方式的一种,与Object的作用类似,?比Object扩展了extends与super。
  • 通配符只能用于填充泛型变量T,不能用于定义变量。

代码如下:

package com.wechat.management.type;

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


public class TypeParam<T> {//泛型变量T用到类上

    public static void main(String[] args) {
        List<?> list;//填充变量类型
        //使用Arrays.asList给list赋值。public static <T> List<T> asList(T... a)
//        list = new ArrayList<?>(Arrays.asList(1,2));//报错!只能用于填充泛型变量T,不能用于定义变量
//        print(list);
        list = new ArrayList<>(Arrays.asList(3L,4L));//?代表Long
        print(list);//class java.lang.Long
        list = new ArrayList<Object>(Arrays.asList("5","6"));//?代表String
        print(list);//class java.lang.String


        List<Object> list2;
        list2 = new ArrayList<Object>(Arrays.asList(1,2));//Object代表Integer
        print(list2);//class java.lang.Integer
        list2 = new ArrayList<Object>(Arrays.asList(3L,4L));//Object代表Long
        print(list2);//class java.lang.Long
        list2 = new ArrayList<Object>(Arrays.asList("5","6"));//Object代表String
        print(list2);//class java.lang.String


        TypeParam typeParam = new TypeParam();
        System.out.println(typeParam.showType(1));//class java.lang.Integer
        System.out.println(typeParam.showType(3L));//class java.lang.Long
        System.out.println(typeParam.showType("5"));//class java.lang.String
    }

    public T showType(T t) {////泛型变量T用到函数(方法)
        return (T) t.getClass();
    }

    public static void print(List<?> list) {//填充变量类型
        for (Object o : list) {
            System.out.println(o.getClass());
        }
    }

}

泛型使用范围

泛型类、泛型接口、泛型方法

泛型类与泛型接口

​ 泛型类和普通类的区别就是类名后有类型参数列表 <E>,既然叫“列表”了,当然这里的类型参数可以有多个,比如 public class HashMap<K, V>,参数名称由开发者决定。

类名中声明参数类型后,内部成员、方法就可以使用这个参数类型,在类名后声明了类型 T,它的成员、方法就可以使用 T 表示成员类型、方法参数/返回值都是 T 类型。

​ 和泛型类一样,泛型接口在接口名后添加类型参数,比如上面的 Map<K, V>,接口声明类型后,接口方法就可以直接使用这个类型。

​ 实现类在实现泛型接口时需要指明具体的参数类型,不然默认类型是 Object,这就失去了泛型接口的意义。比如:HashMap<Long, String> hashMap = new HashMap<Long, String>();HashMap hashMap = new HashMap<>();

​ 泛型类最常见的用途就是作为容纳不同类型数据的容器类,比如 Java 集合容器类。

以Map和HashMap为例:

//泛型接口
public interface Map<K, V> {

    V get(Object var1);

    V put(K var1, V var2);

    V remove(Object var1);

    void putAll(Map<? extends K, ? extends V> var1);

    Set<K> keySet();

    Collection<V> values();

    Set<Map.Entry<K, V>> entrySet();
    
    ...
}

//**泛型类**
public class HashMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, Cloneable, Serializable {  
    ... ...

    public V get(Object var1) {
        HashMap.Node var2;
        return (var2 = this.getNode(hash(var1), var1)) == null ? null : var2.value;
    }

    final HashMap.Node<K, V> getNode(int var1, Object var2) {... ...}

    public boolean containsKey(Object var1) {
        return this.getNode(hash(var1), var1) != null;
    }

    public V put(K var1, V var2) {
        return this.putVal(hash(var1), var1, var2, false, true);
    }

    final V putVal(int var1, K var2, V var3, boolean var4, boolean var5) {... ...}
    
    ... ...
}

HashMap在调用构造方法时,在HashMap类名后加上了<K, V>,K使用了Long,V使用了String

HashMap<Long, String> hashMap = new HashMap<Long, String>();

泛型函数(泛型方法)

泛型方法是指使用泛型的方法,可以直接使用类声明的参数也可以方法内声明类型。

使用类声明的类型:HashMap的put方法就是使用的类声明的参数K与V

public class HashMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, Cloneable, Serializable {  
    ... ...
    public V put(K var1, V var2) {
        return this.putVal(hash(var1), var1, var2, false, true);
    }
    ... ...
}

使用方法内声明的类型:自定义泛型类型需要在方法修饰符后使用<T>声明

package com.wechat.management.type;

public class TypeParam {

    public static <T> void sMethod(T a) {
        System.out.println("自定义泛型类型需要在方法修饰符后使用<T>声明");
    }
    
    public <T> T soMethod(T a) {
        System.out.println("自定义泛型类型需要在方法修饰符后使用<T>声明");
        return a;
    }

    /*
    public void oMethod(T a) {
        System.out.println("自定义泛型类型需要在方法修饰符后使用<T>声明,如没有,则会报错'Cannot resolve symbol 'T''");
    }
    */
}
举报

相关推荐

0 条评论