java 泛型及通配符 T,E,K,V简单介绍

sancaiodm Java 2022-01-08 1526 0

定义

   泛型就是广泛的数据类型,一种可以定义数据类型的类型。也就是说所操作的数据类型被指定为一个参数。

   泛型:就是允许在定义类、接口、方法时使用类型形参。这个类型形参将在声明变量、创建对象、调用方法时动态指定,即传         入实际的类型参数(也叫传入类型实参)。传入的类型实参的类型必须是引用类型。 这种参数类型可以用在类、接口和方法         中,分别被称为泛型类、泛型接口、泛型方法。


   个人理解直白点说:  泛类就是类型参数化,处理的数据类型不是固定的,而是可以作为参数传入 

-----------------------------------------------------------------------------------------------

泛型的声明

   interface 接口<T> {}和 class 类<K,V>{}或是 class 类名 <E>

  如我们常用的 list或 arrayList,我们在初始化时都是List<String>,ArrarList<User>

   其中字母T是Type, K是Key, V是Value,E是Element 的首字母,当然也可以是其它任意字母,只是这几个字母大家一看就都其意思,一个不成文的规定吧,他们不代表值,而是表示数据类型,

  注意:

   【1】 T,V,K,E 只能是引用类型,

    如 List<int> listint = new ArrayList<int>();

    //以上是错误的,因为int不是引用类型,是基本数据类型,这里可以用int的封装类Integer

   【2】在指定泛型具体类型后,可以传入该类型或者其子类类型。

   

泛型的实例化

    在类名的后面指定类型参数的值 ,

    何时指定泛型的具体的类型?一般在创建对象时指定泛型的类型。

    List<String> list = new ArrayList<>();


使用形式:
     List<String> list1 = new ArrayList<>();     

     List<String> list1 = new ArrayList<String>();  

     //以上两种方式都可以,第一种方式后面<>内的string可以省略,编译器会自动推断


泛型好处

   如果在没有使用泛型的情况下,我们也可通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者预知实际参数数据类型,强制类型转换错误的情况,编译器不提示错误,只会在程序运行的时才出现ClassCastException异常,

   泛型的好处就是我们在编译的时候编辑器就能够检查数据类型是否安全,并且所有的强制转换都是自动和隐式的。

----------------------------------------------------------------------------------------------------------------------------------

自定义泛型类

  //在实例化泛型类时,必须指定T,E,X,Y,X的具体类型

  class ShoujiODM<T,E,X,Y,X....>{ //...表示可以有多个泛型

     T[] = new T[100];    //错误,使用泛型的数组不能被初始化 

     T[] ;   //正确

     T t;  //

     类名(E e){//构造方法可以使用泛型

     }

     public void f(X x){  //普通实例方法可以使用泛型

     }

     public <V> void f(String str){  //泛型类中的泛型方法

     }

     //在泛型类中声明了一个泛型方法,使用泛型T,此泛型T是一种全新的类型,可以与泛型类中声明的T不是同一种类型。

     public <T> void show_2(T t){

          System.out.println(t.toString());

     }

     //静态方法与静态属性不能使用类的泛型

     //因为静态是与类相关的,而指定泛型的具体类型是在创建对象的时候指定,

     //类的加载要比对象的创建时间要早,所以在静态成员加载的时候 JVM无法判断泛型的具体类型

     static Y y; 

     public static m(Z z){

     }

     //传入的实参类型需与泛型的类型参数类型相同,即为Integer.

     ShoujiODM<Integer> genericInteger = new ShoujiODM<Integer>(123456);

  }

--------------------------------------------------------------------------------------------------------------------------------

自定义泛型接口

 interface 接口名<A,B,C...>{

      A a;    ///错误Cannot make a static reference to the non-static type T

      //因为接口的属性默认就是以public static 修饰,static属性是不可以使用泛型类型

 }

 //泛型接口的类型在继承接口或是实现接口时确定,若没有指定类型,默认为Object类型

 interface ShoujiOdm<T,X> {
	 void  methodA(T t);
	 public X methodB() ;
}

  interface SubGenericInterface extends ShoujiOdm {//继承时也未指定泛型类型,则默认为object类型

}

public class ShoujiOdmClass implements SubGenericInterface {//则在实现类中泛型类型默认为object类型

	@Override
	public void methodA(Object t) {
		// TODO Auto-generated method stub

	}
	@Override
	public Object methodB() {
		// TODO Auto-generated method stub
		return null;
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub

	}

}
//如果你在SubGenericInterface中(继承接口)未指定具体泛型类型,却在ShoujiOdmClass指定具体泛型类型,这是不正确的
//如下面这样写是不正确的,因为SubGenericInterface不是泛型类型,
public class ShoujiOdmClass implements SubGenericInterface<String,String> {

	@Override
	public void methodA(String t) {
		// TODO Auto-generated method stub

	}
	@Override
	public String methodB() {
		// TODO Auto-generated method stub
		return null;
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub

	}

}
----正确方式    在继承接口中指定泛型的具体类型----
  interface SubGenericInterface extends ShoujiOdm<String,String> {

}

public class ShoujiOdmClass implements SubGenericInterface {
	@Override
	public void methodA(String t) {
		// TODO Auto-generated method stub
		
	}
	@Override
	public String methodB() {
		// TODO Auto-generated method stub
		return null;
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub

	}

	
----正确方式    在实现接口类中指定泛型的具体类型----	
public class ShoujiOdmClass implements ShoujiOdm<String,String> {
	@Override
	public void methodA(String t) {
		// TODO Auto-generated method stub
		
	}
	@Override
	public String methodB() {
		// TODO Auto-generated method stub
		return null;
	}

}

如果实现接口类中不指定泛型的具体类型,则默认为Object类型
public class ShoujiOdmClass implements ShoujiOdm{
	@Override
	public void methodA(Object t) {
		// TODO Auto-generated method stub
		
	}
	@Override
	public Object methodB() {
		// TODO Auto-generated method stub
		return null;
	}

}
等价与下面的实现方式,如果不指定泛型具体,推荐用下面的方式实现,

public class ShoujiOdmClass implements ShoujiOdm<Object, Object>{

	@Override
	public void methodA(Object t) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public Object methodB() {
		// TODO Auto-generated method stub
		return null;
	}

}

--------------------------------------------------------------------------------------------------------------------------

  泛型方法

  语法

  修饰符 <T,E,X,Y...>返回类型 方法名(参数列表)  {    //可以是多个泛型参数

 

  }

  //泛型方法可以定义在普通类中,也可以定义在泛型类中

  //泛型方法的泛型类型在调用时被确定

  //public void methondc(K k)修饰符后没有<T,E,X,Y...>,则m此方法不为泛型方法,只是方法使用了类型

public class GenericTestMain<K,T> {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
         new GenericTestMain<String,ArrayList>().methodd("shoujiodm", 1234.5f, new ArrayList());
	}
	
	public void methonda() {//普通方法
		
	}
	
	public <E> void methondb(E e) {//泛型方法
		
	}
	
	public <E> E methondb(E e) {//泛型方法 返回值为E类型
	     return e;
	}
	//泛型方法,但是此处你虽然声明了泛型E,但后面实际使用的V泛型并没有在其它地方声明,编译器会有错误提示
	public <E> void methondBB(V v) {
		
	}
	//普通方法使用了泛型,此方法并不是泛型方法,因为在方法的返回值前没有<K k>
	public void methondc(K k) {
		
	}
	
	//泛型方法可以使用类声明的泛型,也可以使用方法自己声明的泛型
	public <A,B>void methodd(A a,B b,T t){
		System.out.println(a.getClass());
		System.out.println(b.getClass());
		System.out.println(t.getClass());
	}
	
}

? 无界通配符


上界通配符 < ? extends E>

 用 extends 关键字声明,表示参数化的类型可能是所指定的类型或者是此类型的子类。

 在类型参数中使用 extends 表示这个泛型中的参数必须是 E 或者 E 的子类,这样有两个好处:

 1如果传入的类型不是 E 或者 E 的子类,编译不成功

 2泛型中可以使用 E 的方法,要不然还得强转成 E 才能使用


下界通配符 < ? super E>

 下界: 用 super 进行声明,表示参数化的类型可能是所指定的类型或者是此类型的父类型,直至 Object

 在类型参数中使用 super 表示这个泛型中的参数必须是 E 或者 E 的父类。


<T extends E>与<? extends E>的区别

<T extends E>用于定义类型参数,它是一个确定的类型,可以放在泛型类定义中类名的后面,泛型方法返回值的前面

<? extends E>用于实例化类型参数,它用于实例化泛型变量中的类型参数

T shoujiodm = methond();   // 可以

?shoujiodm = methond();   //不可以


评论