泛型

本文介绍了泛型这一程序设计语言特性,它将类型参数化以实现代码复用。阐述了泛型的语法,说明了泛型在类中操作引用数据类型不确定时的使用场景。还详细讲解了泛型类、泛型接口、泛型方法、静态泛型方法,以及泛型限定和对通配符的限定。

一、概念

:泛型是程序设计语言的一种特性。将类型参数化以达到代码复用提高软件开发工作效率的一种数据类型。泛型类是引用类型,是堆对象,主要是引入了类型参数这个概念。简单来说就是“参数化类型”,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

二、语法

:集合类类名<元素类型> 集合对象名 = new 集合类类名<元素类型>()。且必须是引用数据类型;申明变量的<>必须与new 后面的<>保持一致

三、泛型的使用

:当类中要操作的引用数据并且类型不确定的时候。
在具有相识的行为和属性但是有各自不同的对象群体中可以使用泛型来提高效率。
如:动物 猫,狗,老鼠 都有名字,雌雄,种类,大小,但又各不相同。(Genericity类)

四:泛型类

:class 类名称 <泛型标识:可以随便写任意标识号,标识指定的泛型的类型>{}
最典型的就是各种容器类,如:List、Set、Map。
如果未定义传入的实参类型,那么可以放任何参数
泛型的类型参数只能是类类型,不能是简单类型。
传入的实参类型需要于泛型的类型参数相同

/**
 * 泛型类
* @author tt
 */
 public class Genercity3 {
public static void main(String[] args) {
//传入的实参类型需要于泛型的类型参数相同
 //泛型的类型参数只能是类类型,不能是简单类型。
 Temporary<Integer> intemporary = new Temporary(21,21);
 Temporary<String> stringemporary = new Temporary<>("fvcs",12);
 // 不传入实参类型的时候,默认为object
 Temporary i = new Temporary(21.21,21);
 Temporary y = new Temporary<>("hhhhhh",12);
 //打印一下
 System.out.println("intemporary: "+intemporary.getT());
 System.out.println("stringemporary: "+stringemporary.getT());
 System.out.println("i: "+i.getT());
 System.out.println("y: "+y.getT());
 
 // System.out.println(stringemporary.getClass());
// System.out.println(intemporary.getClass());
// 上面的例子可以证明,在编译之后程序会采取去泛型化的措施。也就是说Java中的泛型,只在编译阶段有效
// 在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。
// 也就是说,泛型信息不会进入到运行时阶段。
// 对此总结成一句话:泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型。
// 不能对确切的泛型类型使用instanceof操作。如下面的操作是非法的,编译时会出错。
//   if(intemporary instanceof Temporary<Integer>){ } 
//泛型通配符
  showTValues(stringemporary);//调用方法
  }
  //通配符  此处’?’是类型实参,而不是类型形参
//此处的?和Number、String、Integer一样都是一种实际的类型,是所有类型的父类。是一种真实的类型。
public static void showTValues(Temporary<?> intemporary) {
 // TODO Auto-generated method stub
 System.out.println(intemporary.getStr());
}
}
//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型
class Temporary<T> {//泛型的成员变量类型为T,T的类型由外部指定
 private T str;//泛型构造方法形参str的类型也为T,T的类型也由外部指定
 private int num;
 public T getStr() {
  return str;
 }
 public void setStr(T str) {
  this.str = str;
 }
 public int getNum() {
  return num;
 }
 public void setNum(int num) {
  this.num = num;
 }
 public Temporary(T str, int num) {
  this.str = str;
  this.num = num;
 }
 public Temporary() {}
 /**
  * 虽然在方法中使用了泛型,但是这并不是一个泛型方法。
  *这只是类中一个普通的成员方法,只不过他的返回值是在声明泛型类已经声明过的泛型。
  *所以在这个方法中才可以继续使用 T 这个泛型。
   * @return
  */
  public T getT() {
  return str;
 }
 }

五:泛型接口:

一种是实行泛型接口的时候就指定具体引用数据类型;
另一种是实现泛型接口的时候仍然不指定引用数据类型,在使用接口实现类的时候在指定泛型的具体数据类型
1 泛型接口的定义格式:
  interface 接口名<声明自定义泛型>{}
2 泛型接口要注意的事项:
A. 接口上自定义的泛型的具体数据类型是在实现一个接口的时候指定的。
 B. 在接口上自定义的泛型如果在实现接口的时候没有指定具体的数据类型,那么默认为Object类型。
3.继承泛型接口:修饰符 class 类名<声明自定义泛型> implements 接口名<声明自定义泛型>{}

/**
 * 泛型接口
 * @author tt
 */
//接口泛型的使用方法
interface tool<T>{
 T test(T t);//T代表test返回的是T类型的值,T代指了一个类型,比如String,Integer等
}
//使用方法有两种:
//  1、子类实现接口时候继续保持泛型
//      2、子类实行接口时候确定好泛型类型
class IGenericity1<T> implements tool<T>{
//子类实现接口时保持泛型
@Override
 public T test(T t) {
  // TODO Auto-generated method stub
  return t;
 }
 public String Hallo() {
  return "Hallo保持泛型";
 }
}
//class IGenericity2<String> implements tool<String>     String
//class IGenericity2 implements tool<String>       Object
class IGenericity2<String> implements tool<String>{
//子类实现接口时确定好泛型类型
@Override
public String test(String t) {
 // TODO Auto-generated method stub
 t = (String) "Hello确定类型";
return t;
}
}
/**
 *  T只是一个代替符号 ,<>里面可以放任意类型
 * 但是泛型的类型参数只能是类类型,不能是简单类型。
 * 未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也加到类中
 * 即:tool1<T> implements tool<T>
* @param <T>
 */
class IGenericity3 implements tool<String>{
 //子类实现接口时确定好泛型类型
 @Override
 public String test(String t) {
  // TODO Auto-generated method stub
  t = (String) "Hello确定类型";
  return t;
 }
 }

六:泛型方法

:为了让不同方法可以操作不同的类型,而且类型还不确定。那么可以将泛型定义在方法上。
是否拥有泛型方法,与其所在的类是否是泛型没有关系。
泛型方法使得该方法能够独立于类而产生变化
泛型类泛型与泛型方法泛型可以同时存在。

/**
 * 泛型方法
 *   泛型指定将运行时的问题转到编译期
 *   避免了强制转换的麻烦
 * @author tt
 */
 public class Genercity2 {
 public static void main(String[] args) {
  List<String> a = new ArrayList<>();//实例化一个集合
  a.add("dsfc");
//  a.add(23);//可知道集合里面的对象是object,字符串和数字等都可以加进去,并且不报错。
//  java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
String object = (String) a.get(1);//
  System.out.println(object);
  //  ArrayList可以存放任意类型,例子中添加了一个String类型,添加了一个Integer类型,再使用时都以String的方式使用,因此程序崩溃了。
//  为了解决类似这样的问题(在编译阶段就可以解决),泛型应运而生
apple ap = new apple();
  GenercityTest<Fruit> genercityTest = new GenercityTest();
   genercityTest.show_1(ap);//虽然定义的是Fruit类,但是apple继承自Fruit,所以不会报错
   Person p = new Person();
//  genercityTest.show_1(p);//编译时会报错,因为定义的是Fruit类,而person类与Furit没有继承关系
  //使用这两个方法都可以,因为是自定义泛型方法
  genercityTest.show_2(ap);
  genercityTest.show_2(p);
   //使用这两个方法都可以  ,因为是自定义泛型方法
  genercityTest.show_3(ap);
  genercityTest.show_3(p);
  }
  }
  class GenercityTest<T>{
 private String name;
 private String sex;
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
 public String getSex() {
  return sex;
 }
 public void setSex(String sex) {
  this.sex = sex;
 }
 public GenercityTest(String name, String sex) {
  this.name = name;
  this.sex = sex;
 }
 public GenercityTest() {}
 /**
 * @param <T> 声明一个泛型T,也可以理解为声明此方法为泛型方法
  * @param c 用来创建泛型T代表的具体对象     Class<T>声明泛型的具体类型
  * @return
  * @throws Exception
  * 说明:
  *     1)public 与 返回值中间<T>非常重要,可以理解为声明此方法为泛型方法。
  *     2)只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
 *     3)<T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
  *     4)与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。
  * 泛型的数量也可以为任意多个 
  *    如:public <T,K> K showKeyName(Generic<T> container){
  */
  public <T> T getObject(Class<T> c) throws Exception{
  //创建泛型对象
  T t = c.newInstance();
  System.out.println("qwertyuiokjhgfdsas");
  return t;
 }
 public static void show_8(Person p){
 }
 public static void show_9(){}
 public void show_1(T t){
         System.out.println(t.toString());
     }
      public void show_7(T t){
         System.out.println(t.toString());
     }
      //在泛型类中声明了一个泛型方法,使用泛型E,这种泛型E可以为任意类型。可以类型与T相同,也可以不同。
    //由于泛型方法在声明的时候会声明泛型<E>,因此即使在泛型类中并未声明泛型,编译器也能够正确识别泛型方法中识别的泛型。
    public <E> void show_3(E t){
        System.out.println(t.toString()+"EEEE");
    }
    //在泛型类中声明了一个泛型方法,使用泛型T,注意这个T是一种全新的类型,可以与泛型类中声明的T不是同一种类型。
    public <T> void show_2(T t){
        System.out.println(t.toString()+"TTTT");
    }
    }
    class Fruit {
 private int id;
 private String name;
 private String bak;
  public Fruit(int id, String name, String bak) {
  this.id = id;
  this.name = name;
  this.bak = bak;
 }
 public Fruit() {}
}
class apple extends Fruit{}
class Person {}

七:静态泛型方法

静态方法有一种情况需要注意一下,那就是在类中的静态方法使用泛型:静态方法无法访问类上定义的泛型;
如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上。
即:如果静态方法要使用泛型的话,必须将静态方法也定义成泛型方法 。
/**

  • 如果在类中定义使用泛型的静态方法,需要添加额外的泛型声明(将这个方法定义成泛型方法)
    • 即使静态方法要使用泛型类中已经声明过的泛型也不可以
  • 如:public static void show(T t){…},此时编译器会提示错误信息:
    “StaticGenerator cannot be refrenced from static context”
    public static void show(T t){}
    */

八、泛型限定

泛型上下边界:在使用泛型的时候,我们还可以为传入的泛型类型实参进行上下边界的限制,
如:类型实参只准传入某种类型的父类或某种类型的子类。
为泛型添加上边界,即传入的类型实参必须是指定类型的子类型

/**
 * 静态泛型方法 和  泛型上下边界
 * @author tt
 */ 
 public class Genercity {
public static void main(String[] args) {
Generic<String> generic1 = new Generic<String>("11111");
Generic<Integer> generic2 = new Generic<Integer>(2222);
Generic<Float> generic3 = new Generic<Float>(2.4f);
Generic<Double> generic4 = new Generic<Double>(2.56);
//这一行代码编译器会提示错误,因为String类型并不是Number类型的子类
// showKeyValue1(generic1);//会报错,因为generic1 是String类型,而方法里是继承的number类型
showKeyValue1(generic2);//不会报错
 showKeyValue1(generic3);
 showKeyValue1(generic4);
 }
 /**
 * 如果在类中定义使用泛型的静态方法,需要添加额外的泛型声明(将这个方法定义成泛型方法)
* 即使静态方法要使用泛型类中已经声明过的泛型也不可以。
 * 如:public static void show(T t){..},此时编译器会提示错误信息:
 */
 //    public static void show(T t){}//会报错,因为没有额外的泛型声明
 public static <T> void show(T t){}
 //注意:不是泛型方法
 public static void showKeyValue1(Generic<? extends Number> obj){
   System.out.println("泛型测试"+obj.getS());
 }
 //在泛型方法中添加上下边界限制的时候,必须在权限声明与返回值之间的<T>上添加上下边界即在泛型声明的时候添加
// public <T> T  getObject(Class<T extends Number> c )//编译器会报错:"Unexpected bound"
/**
  * 通配符上限定的应用
  * @param c
  * @return
  * @throws Exception
  */ 
  public <T extends Number> T getObject(Class<T> c) throws Exception{
  //创建泛型对象
  T t = c.newInstance();
  System.out.println("qwertyuiokjhgfdsas");
  return t;
 }
 }
 class Generic<T>{
 private T s;
 public T getS() {
  return s;
 }
 public void setS(T s) {
  this.s = s;
 }
 public Generic(T s) {
  this.s = s;
 }
 }

九、对通配符的限定

通配符:?也可以理解为占位符
containsAll(Collection<?> c)如果此collection包含指定collection中的所有元素,则返回true
addAll(Collection<? extends E> c) 将指定colltion中的所有元素都添加到此collection中
TreeSet(Comparator<? super E> comparator)构造一个新的空TreeSet,它根据指定比较器进行排序。

<? extends A>:表示泛型的类型只能是A类或者A类的子类。 上限,最高到A类。 <? super A>: 表示泛型的类型只能是A类或者A类的父类。 下限,最低到A类。 * 使用类型通配符可以表示同时是List和List等的引用类型。 * 类型通配符一般是使用?代替具体的类型实参,注意此处是类型实参; * 和Number、String、Integer一样都是一种实际的类型,可以把?看成所有类型的父类。 ``` /** * 泛型限定 * @author tt * */ public class Genercity4 { /** * ?与泛型T在使用上的区别 * 使用泛型T,可以使用泛型实例 * @param al */ public static void print_Coll1(ArrayList al) { Iterator it = al.iterator(); while(it.hasNext()) { T t = it.next(); // t.getClass().ge System.out.println(t); } } /** *使用?占位符,效果与泛型是一样的,但是不能获取具体引用数据类型的实例 * @param al */ public static void print_Coll2(ArrayList<?> al) {

Iterator<?> it = al.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
}
}
class Student extends Person{}
class StuComp implements Comparator{
@Override
public int compare(Person o1, Person o2) {
// TODO Auto-generated method stub
o1 = new Student();
return 0;
}
}

### 十、泛型的好处
泛型是c#2.0的一个新增加的特性,它为使用c#语言编写面向对象程序增加了极大的效力和灵活性。
不会强行对值类型进行装箱和拆箱,或对引用类型进行向下强制类型转换,所以性能得到提高。泛型类和
泛型方法同时具备可重用性、类型安全和效率,这是非泛型类和非泛型方法无法具备的。泛型类通常与集合以及作用于集合的方法一起使用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值