深入理解Java 泛型(Generic) ——1. 相关概念和基本使用(Type Variable, Parameterized Type and etc)

前言

本系列文章包含如下几个topics

  1. Generic相关概念和基本使用(Type Variable,Parameterized Type and etc

  2. Generic的实现:Erasure(擦除)方案

  3. Subtyping and Wildcards(通配符)的应用场景

  4. Enumerated Types

  5. Restrictions on Wildcards and unchecking warning

  6. Covariance and contravariance(协变与逆变)

  7. Array and Reflection

  8. Collection Framework with Generic

Abstraction

本篇文章主要介绍泛型的发展泛型的基本使用,相关概念如:Boxing and Unboxing,Type System、Type Variable、Parameterized Variables。文章首先通过一个泛型的基本例子来展示泛型的出现对Java语言以及程序的影响,接着阐述泛型相关的基本概念,最后介绍基本的使用方法。

Introduction

在wikepedia中,Generic如下定义:

1.Generics are a facility of generic programming that were added to the Java programming language in 2004 within version J2SE 5.0.

2.They were designed to extend Java’s type system to allow “a type or method to operate on objects of various types while providing compile-time type safety

The aspect compile-time type safety was not fully achieved, since it was shown in 2016 that it is not guaranteed in all cases.

  • 在J2SE 5.0出现
  • 扩展Java 类型系统去支持一个类型或方法操作不同的类型对象,并保证编译器类型安全
  • 编译时期的类型安全并没有完全的实现

在Java中,Generic被广泛的应用于Java Collections FrameWork,大大的提高了代码的可读性,以及coding的效率。

在Java Generic出现之前,如果想处理一些不同类型的集合如:IntegerStringLists of String,你可以用同样的类:List 去处理:

list of integers 		           List
list of strings                    List
list of lists of strings           List

但是,不得不面对的一个事实是:必须手动的去保证和处理List里面对象的类型。也即:
you must cast it from Object back to Integer(or String or List)

在Java Generic出现之后,则可以用如下方式来区分不同类型的lists

list of integers					List<Integer>
list of strings						List<String>
list of lists of strings 			List<List<String>>

此时编译器保障了要处理list里面的对象类型即:
compiler no explicit cast back to Integer(or String or List<String>)

Motivation Examples

 List v = new ArrayList();
 v.add("test"); // A String that cannot be cast to an Integer
 Integer i = (Integer)v.get(0); // Run time error

在Generic出来之前,编译如上代码并不会报错,但是在运行第三行代码的时候会 throws a runtime exception (java.lang.ClassCastException) ,如果使用泛型重写以上代码,则变为如下:

 List<String> v = new ArrayList<String>();
 v.add("test");
 Integer i = v.get(0); // (type error)  compilation-time error

第一行代码表示该集合只接受String类型的对象,因此第三行代码会直接在第三行代码抛出异常。

因此Generic的一个重要作用就是:将代码安全性检查提前到编译期

我们再看另外一个求和的例子:

 List<Integer> ints = Arrays.asList(1,2,3); 
 int s = 0;
 for (int n : ints) { s += n; }
 assert s == 6;

该例子中,定义了一个Integer类型的集合,然后通过foreach循环获取集合里面的内容经过boxingunboxing处理后做求和操作。

如果不使用generic相应的代码如下:

 List ints = Arrays.asList( new Integer[] {
 new Integer(1), new Integer(2), new Integer(3)
 } );
 int s = 0;
 for (Iterator it = ints.iterator(); it.hasNext(); ) {
 	 int n = ((Integer)it.next()).intValue();
   s += n; 
 }
 assert s == 6;

相比较使用generic的代码,则需要手动处理list里面对象的类型。

因此:泛型能够省去类型强制转换,并简化代码

比较如上2种程序,可以看到generic通过尖括号为List绑定了对象的类型,因此不需要明确的处理和转换对象的类型。

!!!Generic会隐式的执行强制转换,那么如果转换失败了怎么办?

因此Generic具有如下的保证

Cast-iron guarantee: the implicit casts added by the compilation of generics never fail.

但是这个guarantee也有其局限性就是:它只适用于编译器没有发出unchecked warnings的情况。

在文章开头Generic的定义就表明:compile-time type safety was not fully achieved。

Java Type System

Definition:

这里简单的说一下java的类型系统。定义如下:

In programming languages, a type system is a logical system comprising a set of rules that assigns a property called a type to the various constructs of a computer program, such as variables、expressions、functions or modules.

  • 包含了一组规则
  • 为程序里不同的变量、表达式、函数等赋予一种类型属性
Purpose:

type system有什么作用呢?如下

The main purpose of a type system is to reduce possibilities for bugs in computer programs by defining interfaces between different parts of a computer program, and then checking that the parts have been connected in a consistent way.

  • 降低bug
  • 规范不同接口间调用类型的一致性

这里提到了checking,具体分为statically (at compile time)dynamically(at run time);于此同时就对应着编译报错和运行报错

因此:类型系统通过类型检查来保障程序代码满足一系列的类型一致性规则,因此来确保程序的正确性

java language有如下2种说话:

  1. The java programming language is a strongly typed language
  2. The Java programming is a statically typed language
Strongly Typed Language

那什么是strongly typed language

If a language specification requires its typing rules strongly (i.e., more or less allowing only those automatic type conversions that do not lose information), one can refer to the process as strongly typed, if not, as weakly typed.

简单来说只允许那些不丢失类型的自动类型转换,则为强类型,反之为弱类型。

 int a = 1;
 float b = 2.2;
 System.out.println(a+b);

如上例子中整型a和浮点型b相加,那么整型将自动转换为浮点型。

The more type restrictions that are imposed by the compiler, the more strongly typed a programming language is.

Statically Typed Language

这里得提到Type checking类型检查分为静态类型检查动态类型检查通过分析源代码来保障程序的类型安全则为静态类型检查,相应的语言则为静态类型语言。

Static type checking is the process of verifying the type safety of a program based on analysis of a program’s text (source code).

如果一段代码不能通过静态类型检查,则会抛出编译期异常。

Concepts About Generic

Type Variable
  1. A type variable is an unqualified identifier used as a type in class, interface, method, and constructor bodies.
  2. A type variable is introduced by the declaration of a type parameter of a generic class, interface, method or constructor
  • 类型变量是一个标识符用于类,接口,方法和构造函数
  • 类型变量通过泛型的类型参数的声明引入

因此,我们有:

A class (interfacemethodconstructor) is generic if it declares one or more type variable. These type variables are known as the type parameters of the class (interfacemethodconstructor) . It define one or more type variables that act as parameters.

Definition

TypeParameter := {TypeParameterModifier} Identifier [TypeBound]

TypeParameterModifier := Annotation

TypeBound := extends TypeVariable extends ClassOrInterfaceType {AdditionalBound}

*AdditionalBound := *& InterfaceType

说明如下:

  • TypeParameter 可以是单独的标识符号如:T
  • 或者可以为T1 & … & Tn
  • 每个T 可以声明一个边界:T extend Number
  • 如果没有声明边界,则默认为object
Example
  package TypeVarMembers;
 
  class C {
    public    void mCPublic()    {}
    private   void mCPrivate()   {}
  }
 
  interface I {
    void mI();
  }
 
  class CT extends C implements I {
	   public void mI() {}
  }
 
  class Test {
    	<T extends C & I> void test(T t) {
    	t.mI();           // OK
    	t.mCPublic();     // OK
    	t.mCPrivate();    // Compile-time error
   } 
  }

1.类C有一个public方法和private方法; 
2.接口I定义了方法mi(); 
3.类CT继承了类C,实现了接口I, 因此包含类C和接口I的所有属性和方法
4.类Test定义了一个泛型方法 test; 该方法声明了一个类型变量T; 
  T通过extends声明了它的边界 C & T; 因此与CT一样,T继承了C和T的属性和方法。

Parameterized Types
  1. A class or interface declaration that is generic defines a set of parameterized types.
  2. A parameterized type is a class or interface type of the form C<T1,…,Tn>, where C is the name of a generic type and <T1,…,Tn> is a list of type arguments that denote
    a particular parameterization of the generic type.
因此对于 C<T1,...,Tn>
1. C is the name of a generic type.
2. T1 ... Tn is a list of type arguments
3. the number of type arguments must match the number type parameters.
4. each T is a parameterized type
5. each T has its bound.(Object if not be declared)

Example

List<String>				//ok
List<List<Integer> 			//ok
Map<String>					// error, not enough type arument
Map<String,Integer> 		//ok
List<int>					//error, primitive types cannot be type arguments 
Pair<String,String,String> //error too many arguments

Boxing and UnBoxing

Java 类型分为reference type or a primitive type. 所有的reference types 都是Object的子类,可以被赋值为null

Primitive					Reference
byte							Byte
short							Short
int								Integer
long							Long
float							Float
double						Double
boolean						Boolean
char							Character

Boxing: Conversion of a primitive type to the corresponding reference type

UnBoxing: conversion of the reference type to the corresponding primitive type

泛型会在合适的地方自动的插入boxing or unboxing 如下相等的2个例子

 List<Integer> ints = new ArrayList<Integer>(); ints.add(1);
 int n = ints.get(0);

 List<Integer> ints = new ArrayList<Integer>(); 
 ints.add(Integer.valueOf(1));
 int n = ints.get(0).intValue();

Foreach
 List<Integer> ints = Arrays.asList(1,2,3); int s = 0;
 for (int n : ints) { s += n; } 
   //foreach loop even though with the keyword for.
 assert s == 6;

第三行代码等价于如下代码:

 for (Iterator<Integer> it = ints. iterator();it.hasNext();) { 
   int n = it.next();
 	 s += n;
 }

Generic 基本使用

Class
public interface List<E> extends Collection<E> {
  Iterator<E> iterator();
  boolean add(E e);
  E get(int index);
  E set(int index, E element);
  boolean addAll(Collection<? extends E> c);
  int indexOf(Object o);
}

1.List是一个泛型接口,List 是对应的接口名字
2.List定一个了一个parameterized Type 为 Type Variable E;
3.bound默认为Object.
4.继承了Collection<E> 接口
5.包含了一系列的方法,这里关注下  
	boolean addAll(Collection<? extends E> c);
		因为父类可以引用它的子类,所以用 <? extends E>来表示。
	int indexOf(Object o);
		这里传入参数的类型是Object, 为什么不是indexOf(E o),因为判断传入的类型可以是任意的,他们只有一个子类Object;如果定义为 indexOf(E o)那么传入的方法不是E或者E的子类的时候就会报 unchecking warning.



Method

这里为以上的List的实现类加一个Generic Method

public class MyList<E> implements List<E>{
    public static <K,V> List<K> toKeyList(Map<? extends K,V> map){
        List<K> list = new ArrayList<>();
        for(K k:map.keySet()){
            list.add(k);
        }
        return list;
    }
 	1.toKeyList泛型函数定义了2个parameterized Type, Type Variable K and V
  	2.传入一个Map map的key值为K以及K的子类,这里把它转换为List.
}

以上就是基本的泛型使用。

Conclusion

本文简单的介绍了Generic相关的基本概念以及其基本使用方法,那么对应日常应用场景有什么用呢?

  1. 优秀的开源框架,或多或少都涉及到泛型,帮助你更好的理解开源框架的设计思想
  2. 遇到泛型相关的bug(类型出错),能更精准的定位问题
  3. Generic涉及了很多优秀的语言设计思想,可以提高自己的编码思维,大大提高了代码的扩展性
  4. more….
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 这个错误是因为在Java中,一个类不能被强制转换为ParameterizedType类型。通常是因为在使用反射时,尝试将一个Class对象转换为ParameterizedType对象,但是这个Class对象实际上不是一个ParameterizedType类型。要解决这个问题,需要检查代码中的反射使用,确保正确地使用ParameterizedType类型。 ### 回答2: java.lang.Class无法强制转换为java.lang.reflect.ParameterizedType。 在Java中,java.lang.Class类表示运行时类的信息和属性,而java.lang.reflect.ParameterizedType接口表示带有参数的类型(泛型类型)。 这个错误是因为在尝试将Class对象强制转换为ParameterizedType对象时,类型转换发生了错误。 要解决这个问题,我们需要理解java.lang.reflect.ParameterizedType使用情况。 ParameterizedType接口是Type接口的子接口,可以用于获取带有参数的类型信息,例如泛型类或泛型接口的类型参数信息。 如果我们想要获得一个类的泛型参数类型信息,我们可以使用java.lang.Class类中的getGenericSuperclass()方法或getGenericInterfaces()方法。 但是要注意,这些方法返回的类型是java.lang.reflect.Type,而不是java.lang.reflect.ParameterizedType。 如果我们想要将Type对象转换为ParameterizedType对象,我们需要进行类型检查和类型转换。 示例代码: import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; public class MyClass<T> { public MyClass() { Type type = getClass().getGenericSuperclass(); if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; // 在这里可以使用ParameterizedType对象来获取泛型参数类型信息 } } } 总结来说,java.lang.Class不能直接强制转换为java.lang.reflect.ParameterizedType,我们需要根据具体的使用情况进行类型检查和转换才能正确地获取泛型参数类型信息。 ### 回答3: java.lang.Class不能被转换为java.lang.reflect.ParameterizedType的原因是两者表示的是不同的类型信息。java.lang.Class用于表示类的信息,而java.lang.reflect.ParameterizedType用于表示泛型类型的信息。 当使用强制类型转换将一个对象转换为java.lang.reflect.ParameterizedType时,如果该对象实际上是java.lang.Class类型的对象,就会抛出ClassCastException。这是因为java.lang.Class和java.lang.reflect.ParameterizedType是不兼容的类型。 如果需要获取一个类中的泛型类型信息,应该使用java.lang.Class对象的getGenericSuperclass()方法来获取其父类的泛型类型信息,再通过强制类型转换获取到java.lang.reflect.ParameterizedType对象。 例如: ``` class MyGenericClass<T> { } class MyClass extends MyGenericClass<String> { } public static void main(String[] args) { MyClass myClass = new MyClass(); ParameterizedType parameterizedType = (ParameterizedType) myClass.getClass().getGenericSuperclass(); Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); Type actualTypeArgument = actualTypeArguments[0]; String genericType = actualTypeArgument.getTypeName(); System.out.println(genericType); } ``` 如果在强制类型转换时抛出了java.lang.ClassCastException,可以检查代码中的类型转换部分,并确保正确使用泛型

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值