JAVA基础-泛型

概述

  • Java 泛型(generics)是JDK5中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
  • 在创建对象或调用方法的时候才明确下具体的类型,好处就是在编译的时候能够检查类型安全,并且所有的强制转换都是自动和隐式的。

使用方法

  • 你可以写一个泛型方法,该方法在调用时可以接收不同类型的参数。根据传递给泛型方法的参数类型,编译器适当地处理每一个方法调用。
  • 下面是定义泛型方法的规则:
    • 所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(在下面例子中的 )。
    • 每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
    • 类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
    • 泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像 int、double、char 等)。

泛型中通配符:

  • 本质上这些个都是通配符,没啥区别,只不过是编码时的一种约定俗成的东西。换成其他的字母代替 T ,在可读性上可能会弱一些。通常情况下,T,E,K,V,?是这样约定的:
    • E - Element (在集合中使用,因为集合中存放的是元素)
    • T - Type(Java 类)
    • K - Key(键)
    • V - Value(值)
    • N - Number(数值类型)
    • ? - 表示不确定的 java 类型
实例
下面的例子演示了如何使用泛型方法打印不同类型的数组元素:
public class GenericMethodTest
{
   // 泛型方法 printArray                         
   public static < E > void printArray( E[] inputArray )
   {
      // 输出数组元素            
         for ( E element : inputArray ){        
            System.out.printf( "%s ", element );
         }
         System.out.println();
    }
 
    public static void main( String args[] )
    {
        // 创建不同类型数组: Integer, Double 和 Character
        Integer[] intArray = { 1, 2, 3, 4, 5 };
        Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
        Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };
 
        System.out.println( "整型数组元素为:" );
        printArray( intArray  ); // 传递一个整型数组
 
        System.out.println( "\n双精度型数组元素为:" );
        printArray( doubleArray ); // 传递一个双精度型数组
 
        System.out.println( "\n字符型数组元素为:" );
        printArray( charArray ); // 传递一个字符型数组
    } 
}

泛型参数声明

  • 可能有时候,你会想限制那些被允许传递到一个类型参数的类型种类范围。例如,一个操作数字的方法可能只希望接受Number或者Number子类的实例。这就是有界类型参数的目的。
  • 要声明一个有界的类型参数,首先列出类型参数的名称,后跟extends关键字,最后紧跟它的上界。
实例
下面的例子演示了"extends"如何使用在一般意义上的意思"extends"(类)或者"implements"(接口)。该例子中的泛型方法返回三个可比较对象的最大值。

public class MaximumTest
{
   // 比较三个值并返回最大值
   public static <T extends Comparable<T>> T maximum(T x, T y, T z)
   {                     
      T max = x; // 假设x是初始最大值
      if ( y.compareTo( max ) > 0 ){
         max = y; //y 更大
      }
      if ( z.compareTo( max ) > 0 ){
         max = z; // 现在 z 更大           
      }
      return max; // 返回最大对象
   }
   public static void main( String args[] )
   {
      System.out.printf( "%d, %d 和 %d 中最大的数为 %d\n\n",
                   3, 4, 5, maximum( 3, 4, 5 ) );
 
      System.out.printf( "%.1f, %.1f 和 %.1f 中最大的数为 %.1f\n\n",
                   6.6, 8.8, 7.7, maximum( 6.6, 8.8, 7.7 ) );
 
      System.out.printf( "%s, %s 和 %s 中最大的数为 %s\n","pear",
         "apple", "orange", maximum( "pear", "apple", "orange" ) );
   }
}

泛型类声明

  • 泛型类的声明和非泛型类的声明类似,除了在类名后面添加了类型参数声明部分。和泛型方法一样,泛型类的类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。因为他们接受一个或多个参数,这些类被称为参数化的类或参数化的类型。
public class Box<T> {
   
  private T t;
 
}

类型通配符

?无界通配符

  • 对于不确定或者不关心实际要操作的类型,可以使用无限制通配符(尖括号里一个问号,即 <?> ),表示可以持有任何类型。
  • 例如 List<?> 在逻辑上是 List,List 等所有 List<具体类型实参> 的父类。
  public static void getData(List<?> data) {
     System.out.println("data :" + data.get(0));
  }
  • 我有一个父类 Animal 和几个子类,如狗、猫等,现在我需要一个动物的列表:
List<Animal> listAnimals

List<? extends Animal> listAnimals

通配符其实在声明局部变量时是没有什么意义的,但是当你为一个方法声明一个参数时,它是非常重要的。
static int countLegs (List<? extends Animal > animals ) {
    int retVal = 0;
    for ( Animal animal : animals )
    {
        retVal += animal.countLegs();
    }
    return retVal;
}

上界通配符 < ? extends E>

  • 用 extends 关键字声明,表示参数化的类型可能是所指定的类型,或者是此类型的子类。
  • 类型通配符上限通过形如List来定义,如此定义就是通配符泛型值接受Number及其下层子类类型。
public static void getUperNumber(List<? extends Number> data) {
      System.out.println("data :" + data.get(0));
   }
  • 在类型参数中使用 extends 表示这个泛型中的参数必须是 E 或者 E 的子类,这样有两个好处:

    • 如果传入的类型不是 E 或者 E 的子类,编译不成功
    • 泛型中可以使用 E 的方法,要不然还得强转成 E 才能使用
  • 类型参数列表中如果有多个类型参数上限,用逗号分开

private <K extends A, E extends B> E test(K arg1, E arg2){
    E result = arg2;
    arg2.compareTo(arg1);
    //.....
    return result;
}

下界通配符 < ? super E>

  • 用 super 进行声明,表示参数化的类型可能是所指定的类型,或者是此类型的父类型,如 Object 类型的实例。
  • 在类型参数中使用 super 表示这个泛型中的参数必须是 E 或者 E 的父类。

private <T> void test(List<? super T> dst, List<T> src){
    for (T t : src) {
        dst.add(t);
    }
}

public static void main(String[] args) {
    List<Dog> dogs = new ArrayList<>();
    List<Animal> animals = new ArrayList<>();
    new Test3().test(animals,dogs);
}
// Dog 是 Animal 的子类
class Dog extends Animal {

}

?和 T 的区别

  • T 是一个 确定的 类型,通常用于泛型类和泛型方法的定义,?是一个 不确定 的类型,通常用于泛型方法的调用代码和形参,不能用于定义类和泛型方法。
  • 区别1:通过 T 来 确保 泛型参数的一致性;
//通过T来确保泛型参数的一致性
public <T extends Number>void
test(List<T>dest,List<T>src)

/通配符是不确定的,所以这个方法不能保证两个List具有相同的元素类型
public void
test(List<?extends Number>dest,List<?extends Number>src)
  • 区别2:类型参数可以多重限定而通配符不行, 使用 & 符号设定多重边界(Multi Bounds),指定泛型类型 T 必须是 MultiLimitInterfaceA 和 MultiLimitInterfaceB 的共有子类型,此时变量 t 就具有了所有限定的方法和属性。对于通配符来说,因为它不是一个确定的类型,所以不能进行多重限定。
public class MultiLimit implements MultiLimitInterfaceA,MultiLimitInterfaceB
	public static<T extends MultiLimitInterfaceA MultiLimitInterfaceB>void test(Tt){
}

*接口A
interface MultiLimitInterfaceA {
}

*接口B
interface MultiLimitInterfaceB {
}
  • 区别3:通配符可以使用超类限定而类型参数不行
类型参数 T 只具有 一种 类型限定方式:
T extends A

但是通配符 ? 可以进行 两种限定:
? extends A
? super A

Class和 Class<?>区别

  • Class在实例化的时候,T 要替换成具体类。Class<?>它是个通配泛型,? 可以代表任何类型,所以主要用于声明时的限制情况。
  • 当不知道定声明什么类型的 Class 的时候可以定义一 个Class<?>。
public class Test3{
	/可以
	public Class<?>clazz;
	//不可以,因为T需要指定类型
	public Class<T>clazzT;
}
  • 那如果也想 public Class clazzT;这样的话,就必须让当前的类也指定 T ,
public class Test3<T>{
	public Class<?>clazz;
	//不会报错
	public Class<T>clazzT;
}

参考文档

  • https://www.runoob.com/java/java-generics.html 菜鸟教程
  • https://mp.weixin.qq.com/s/sS0wXGGQ0IK7LaeDMtSarQ
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值