1、晓之以理,动之以码

学Java就是很上头哦,一来直接三连问!!!

什么是泛型?为什么要用泛型?泛型怎么用?

当然泛型在Java中有很重要的一个地位,在面向对象编程以及在各种设计模式中有非常广泛的应用~

1.1 什么是泛型?

泛型:
本意就是“参数化类型”。就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。

1.2  一个栗子

一个被举烂的例子

package com.example.main;

/**
* @author lin
* @version 1.0
* @date 2020/7/12 21:31
* @Description TODO
*/
public class TestTypeMain {
   private static int add(int a, int b) {
       System.out.println(a + "+" + b + "=" + (a + b));
       return a + b;
  }    private static float add(float a, float b) {
       System.out.println(a + "+" + b + "=" + (a + b));
       return a + b;
  }    private static double add(double a, double b) {
       System.out.println(a + "+" + b + "=" + (a + b));
       return a + b;
  }    private static <T extends Number> double add(T a, T b) {
       System.out.println(a + "+" + b + "=" + (a.doubleValue() + b.doubleValue()));
       return a.doubleValue() + b.doubleValue();
  }    public static void main(String[] args) {
       System.out.println(TestTypeMain.add(1, 2));
       System.out.println(TestTypeMain.add(1f, 2f));
       System.out.println(TestTypeMain.add(1d, 2d));
       System.out.println(TestTypeMain.add(Integer.valueOf(1), Integer.valueOf(2)));
       System.out.println(TestTypeMain.add(Float.valueOf(1), Float.valueOf(2)));
       System.out.println(TestTypeMain.add(Double.valueOf(1), Double.valueOf(2)));
  } }
运行结果:
   1+2=3
   3
   1.0+2.0=3.0
   3.0
   1.0+2.0=3.0
   3.0
   1+2=3.0
   3.0
   1.0+2.0=3.0
   3.0
   1.0+2.0=3.0
   3.0

1.3 总结(意义):

1:减少代码,增强可读性,代码复用性更高

2:显性指定类型,编译器会自动检查,避免运行中的一半的错误。

2、泛型如何使用?

2.1 泛型可以用到哪里?

1、泛型方法:public static < E > void printArray( E[] inputArray )

public static <T extends Comparable<T>> T maximum(T x, T y, T z)

2、泛型类:public class Box<T> {  private T t; }

3、泛型接口:public interface Generator<T> { public T next(); }

4、泛型通配符:List<String> list= new ArrayList<String>();

2.2 泛型类型的变量

E:元素(Element),多用于java集合框架
K:关键字(Key)
N:数字(Number)
T:类型(Type)
V:值(Value)

2.3 泛型方法

简单的泛型方法入门可以参考上面的那个栗子. 另外通过一个例子,把泛型方法再总结一下。

   /**
    * 这才是一个真正的泛型方法。
    * 首先在public与返回值之间的<T>必不可少,这表明这是一个泛型方法,并且声明了一个泛型T
    * 这个T可以出现在这个泛型方法的任意位置.
    * 泛型的数量也可以为任意多个
    *   如:public <T,K> K showKeyName(Generic<T> container){
    *       ...
    *       }
    */
   public <T> T showKeyName(Generic<T> container){
       System.out.println("container key :" + container.getKey());
       //当然这个例子举的不太合适,只是为了说明泛型方法的特性。
       T test = container.getKey();
       return test;
  }    //这也不是一个泛型方法,这就是一个普通的方法,只是使用了Generic<Number>这个泛型类做形参而已。
   public void showKeyValue1(Generic<Number> obj){
       Log.d("泛型测试","key value is " + obj.getKey());
  }    //这也不是一个泛型方法,这也是一个普通的方法,只不过使用了泛型通配符?
   //同时这也印证了泛型通配符章节所描述的,?是一种类型实参,可以看做为Number等所有类的父类
   public void showKeyValue2(Generic<?> obj){
       Log.d("泛型测试","key value is " + obj.getKey());
  }     /**
    * 这个方法是有问题的,编译器会为我们提示错误信息:"UnKnown class 'E' "
    * 虽然我们声明了<T>,也表明了这是一个可以处理泛型的类型的泛型方法。
    * 但是只声明了泛型类型T,并未声明泛型类型E,因此编译器并不知道该如何处理E这个类型。
   public <T> T showKeyName(Generic<E> container){
       ...
   }  
   */    /**
    * 这个方法也是有问题的,编译器会为我们提示错误信息:"UnKnown class 'T' "
    * 对于编译器来说T这个类型并未项目中声明过,因此编译也不知道该如何编译这个类。
    * 所以这也不是一个正确的泛型方法声明。
   public void showkey(T genericObj){    }
   */

此外:

   /**
    * 这才是一个真正的泛型方法。
    * 首先在public与返回值之间的<T>必不可少,这表明这是一个泛型方法,并且声明了一个泛型T
    * 这个T可以出现在这个泛型方法的任意位置.
    * 泛型的数量也可以为任意多个
    *   如:public <T,K> K showKeyName(Generic<T> container){
    *       ...
    *       }
    */
   public <T> T showKeyName(Generic<T> container){
       System.out.println("container key :" + container.getKey());
       //当然这个例子举的不太合适,只是为了说明泛型方法的特性。
       T test = container.getKey();
       return test;
  }    //这也不是一个泛型方法,这就是一个普通的方法,只是使用了Generic<Number>这个泛型类做形参而已。
   public void showKeyValue1(Generic<Number> obj){
       Log.d("泛型测试","key value is " + obj.getKey());
  }    //这也不是一个泛型方法,这也是一个普通的方法,只不过使用了泛型通配符?
   //同时这也印证了泛型通配符章节所描述的,?是一种类型实参,可以看做为Number等所有类的父类
   public void showKeyValue2(Generic<?> obj){
       Log.d("泛型测试","key value is " + obj.getKey());
  }     /**
    * 这个方法是有问题的,编译器会为我们提示错误信息:"UnKnown class 'E' "
    * 虽然我们声明了<T>,也表明了这是一个可以处理泛型的类型的泛型方法。
    * 但是只声明了泛型类型T,并未声明泛型类型E,因此编译器并不知道该如何处理E这个类型。
   public <T> T showKeyName(Generic<E> container){
       ...
   }  
   */    /**
    * 这个方法也是有问题的,编译器会为我们提示错误信息:"UnKnown class 'T' "
    * 对于编译器来说T这个类型并未项目中声明过,因此编译也不知道该如何编译这个类。
    * 所以这也不是一个正确的泛型方法声明。
   public void showkey(T genericObj){    }
   */

2.4 泛型类

定义一个泛型类:public class GenericClass{}

package com.example.main;

/**
* @author lin
* @version 1.0
* @date 2020/7/12 21:49
* @Description 泛型类
*/
public class GenericClass<T> {
   private T data;    public T getData() {
       return data;
  }    public void setData(T data) {
       this.data = data;
  }    public static void main(String[] args) {
       GenericClass<String> genericClass = new GenericClass<>();
       genericClass.setData("Generic Class");
       System.out.println(genericClass.getData());
  }
}

2.5 泛型接口

定义一个泛型接口:public interface GenericIntercace{}

package com.example.main;

/**
* @author lin
* @version 1.0
* @date 2020/7/12 21:50
* @Description TODO
*/
public interface GenericInterface<T> {
  public T getData();
}

实现泛型接口不指定类型方式一:

public class ImplGenericInterface1 implements GenericIntercace

package com.example.main;

/**
* @author lin
* @version 1.0
* @date 2020/7/12 21:53
* @Description TODO
*/
public class ImplGenericInterface1<T> implements GenericInterface<T> {
   private T data;    private void setData(T data) {
       this.data = data;
  }    @Override
   public T getData() {
       return this.data;
  }    public static void main(String[] args) {
       ImplGenericInterface1<String> implGenericInterface1 = new ImplGenericInterface1<>();
       implGenericInterface1.setData("Generic Interface1");
       System.out.println(implGenericInterface1.getData());
  }
}

实现泛型接口指定类型方式二:

public class ImplGenericInterface2 implements GenericIntercace<String> {}

package com.example.main;

/**
* @author lin
* @version 1.0
* @date 2020/7/12 21:55
* @Description TODO
*/
public class ImplGenericInterface2 implements GenericInterface<String> {
   @Override
   public String getData() {
       return "hello world ";
  }    public static void main(String[] args) {
       ImplGenericInterface2 implGenericInterface2 = new ImplGenericInterface2();
       System.out.println(implGenericInterface2.getData());
  }
}

2.6 泛型通配符

引入通配符可以在泛型实例化时更加灵活地控制,也可以在方法中控制方法的参数。

语法如下:

泛型类名<? extends T> 或 泛型类名<? super T> 或 泛型类名<?>? extends T:表示T或T的子类

? super T:表示T或T的父类

?:表示可以是任意类型

常用的 T,E,K,V,?

本质上这些个都是通配符,没啥区别,只不过是编码时的一种约定俗成的东西。比如上述代码中的 T ,我们可以换成 A-Z 之间的任何一个 字母都可以,并不会影响程序的正常运行,但是如果换成其他的字母代替 T ,在可读性上可能会弱一些。通常情况下,T,E,K,V,?是这样约定的:

2.6.1、?表示不确定的 java 类型

static int countLegs (List<? extends Animal > animals ) {
   int retVal = 0;
   for ( Animal animal : animals )
  {
       retVal += animal.countLegs();
  }
   return retVal;
} static int countLegs1 (List< Animal > animals ){
   int retVal = 0;
   for ( Animal animal : animals )
  {
       retVal += animal.countLegs();
  }
   return retVal;
}

2.6.2、T (type) 表示具体的一个java类型

下界: 用 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);
}

实例:

class gent<T> {
   public void test() {
       System.out.println("gent");
  }
} class supC {
   public String toString() {
       return "supA";
  }
} public class Bc extends supC {
   String b;    public Bc( String b ) {
       this.b = b;
  }    public String toString() {
       return "subB";
  }    // 指明泛型参数必须是supC或其子类
   public void test( gent<? extends supC> o ) {
       System.out.println("Bc");
  }    public static void main( String[] args ) {
       Bc bc = new Bc("test");
       gent<Bc> oGent = new gent<Bc>();
       bc.test(oGent);    // oGent 是supC的子类对象
  }
}

3、泛型注意点

3.1 静态方法与泛型

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

即:如果静态方法要使用泛型的话,必须将静态方法也定义成泛型方法 。

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

3.2 泛型类型继承规则

1,对于泛型参数是继承关系的泛型类之间是没有继承关系的 2,泛型类可以继承其它泛型类,例如: public class ArrayList<E> extends AbstractList<E> 3,泛型类的继承关系在使用中同样会受到泛型类型的影响

/**
* Author:lin 2020/7/12 21:55
* <p>
* Description: 泛型继承规则测试类
*/
public class GenericInherit<T> {
   private T data1;
   private T data2;    public T getData1() {
       return data1;
  }    public void setData1(T data1) {
       this.data1 = data1;
  }    public T getData2() {
       return data2;
  }    public void setData2(T data2) {
       this.data2 = data2;
  }    public static <V> void setData2(GenericInherit<Father> data2) {   }    public static void main(String[] args) {
//       Son 继承自 Father
       Father father = new Father();
       Son son = new Son();
       GenericInherit<Father> fatherGenericInherit = new GenericInherit<>();
       GenericInherit<Son> sonGenericInherit = new GenericInherit<>();
       SubGenericInherit<Father> fatherSubGenericInherit = new SubGenericInherit<>();
       SubGenericInherit<Son> sonSubGenericInherit = new SubGenericInherit<>();        /**
        * 对于传递的泛型类型是继承关系的泛型类之间是没有继承关系的
        * GenericInherit<Father> 与GenericInherit<Son> 没有继承关系
        * Incompatible types.
        */
       father = new Son();
//       fatherGenericInherit=new GenericInherit<Son>();        /**
        * 泛型类可以继承其它泛型类,例如: public class ArrayList<E> extends AbstractList<E>
        */
       fatherGenericInherit=new SubGenericInherit<Father>();        /**
        *泛型类的继承关系在使用中同样会受到泛型类型的影响
        */
       setData2(fatherGenericInherit);
//       setData2(sonGenericInherit);
       setData2(fatherSubGenericInherit);
//       setData2(sonSubGenericInherit);   }    private static class SubGenericInherit<T> extends GenericInherit<T> {   }

3.3 虚拟机是如何实现泛型的

Java泛型是Java1.5之后才引入的,为了向下兼容。Java采用了C++完全不同的实现思想。Java中的泛型更多的看起来像是编译期用的 Java中泛型在运行期是不可见的,会被擦除为它的上级类型。如果是没有限定的泛型参数类型,就会被替换为Object.

GenericClass<String> stringGenericClass=new GenericClass<>();
GenericClass<Integer> integerGenericClass=new GenericClass<>();

C++中GenericClass<String>和GenericClass<Integer>是两个不同的类型 Java进行了类型擦除之后统一改为GenericClass<Object>

/**
* Author:lin 2020/7/12 21:55
* <p>
* Description:泛型原理测试类
*/
public class GenericTheory {
   public static void main(String[] args) {
       Map<String, String> map = new HashMap<>();
       map.put("Key", "Value");
       System.out.println(map.get("Key"));
       GenericClass<String, String> genericClass = new GenericClass<>();
       genericClass.put("Key", "Value");
       System.out.println(genericClass.get("Key"));
  }    public static class GenericClass<K, V> {
       private K key;
       private V value;        public void put(K key, V value) {
           this.key = key;
           this.value = value;
      }        public V get(V key) {
           return value;
      }
  }    /**
    * 类型擦除后GenericClass2<Object>
    * @param <T>
    */
   private class GenericClass2<T> {   }    /**
    * 类型擦除后GenericClass3<ArrayList>
    * 当使用到Serializable时会将相应代码强制转换为Serializable
    * @param <T>
    */
   private class GenericClass3<T extends ArrayList & Serializable> {   }
}

对应的字节码文件

public static void main(String[] args) {
       Map<String, String> map = new HashMap();
       map.put("Key", "Value");
       System.out.println((String)map.get("Key"));
       GenericTheory.GenericClass<String, String> genericClass = new GenericTheory.GenericClass();
       genericClass.put("Key", "Value");
       System.out.println((String)genericClass.get("Key"));
  }

4.学以致用

1,泛型解析JSON数据封装

api返回的json数据

{
   "code":200,
   "msg":"成功",
   "data":{
       "name":"Lin",
       "email":"10086"
  }
}
BaseResponse .java
/**
* Author:lin 2020/7/12 21:55
* <p>
* Description: 接口数据接收基类
*/
public class BaseResponse {    private int code;
   private String msg;    public int getCode() {
       return code;
  }    public void setCode(int code) {
       this.code = code;
  }    public String getMsg() {
       return msg;
  }    public void setMsg(String msg) {
       this.msg = msg;
  }
}
UserResponse.java
/**
* Author:lin 2020/7/12 21:55
* <p>
* Description: 用户信息接口实体类
*/
public class UserResponse<T> extends BaseResponse {
   private T data;    public T getData() {
       return data;
  }    public void setData(T data) {
       this.data = data;
  }
}

2,泛型+反射实现巧复用工具类

/**
* Author:lin 2020/7/12 21:55
* <p>
* Description: 泛型相关的工具类
*/
public class GenericUtils {    public static class Movie {
       private String name;
       private Date time;        public String getName() {
           return name;
      }        public Date getTime() {
           return time;
      }        public Movie(String name, Date time) {
           this.name = name;
           this.time = time;
      }        @Override
       public String toString() {
           return "Movie{" + "name='" + name + '\'' + ", time=" + time + '}';
      }
  }    public static void main(String[] args) {
       List<Movie> movieList = new ArrayList<>();
       for (int i = 0; i < 5; i++) {
           movieList.add(new Movie("movie" + i, new Date()));
      }
       System.out.println("排序前:" + movieList.toString());        GenericUtils.sortAnyList(movieList, "name", true);
       System.out.println("按name正序排:" + movieList.toString());        GenericUtils.sortAnyList(movieList, "name", false);
       System.out.println("按name逆序排:" + movieList.toString());
  }    /**
    * 对任意集合的排序方法
    * @param targetList 要排序的实体类List集合
    * @param sortField 排序字段
    * @param sortMode   true正序,false逆序
    */
   public static <T> void sortAnyList(List<T> targetList, final String sortField, final boolean sortMode) {
       if (targetList == null || targetList.size() < 2 || sortField == null || sortField.length() == 0) {
           return;
      }
       Collections.sort(targetList, new Comparator<Object>() {
           @Override
           public int compare(Object obj1, Object obj2) {
               int retVal = 0;
               try {
                   // 获取getXxx()方法名称
                   String methodStr = "get" + sortField.substring(0, 1).toUpperCase() + sortField.substring(1);
                   Method method1 = ((T) obj1).getClass().getMethod(methodStr, null);
                   Method method2 = ((T) obj2).getClass().getMethod(methodStr, null);
                   if (sortMode) {
                       retVal = method1.invoke(((T) obj1), null).toString().compareTo(method2.invoke(((T) obj2), null).toString());
                  } else {
                       retVal = method2.invoke(((T) obj2), null).toString().compareTo(method1.invoke(((T) obj1), null).toString());
                  }
              } catch (Exception e) {
                   System.out.println("List<" + ((T) obj1).getClass().getName() + ">排序异常!");
                   e.printStackTrace();
              }
               return retVal;
          }
      });
  }
}

3,Gson库中的泛型的使用-TypeToken

/**
* Author:Jay On 2019/5/11 22:11
* <p>
* Description: Gson库中的泛型使用
*/
public class GsonGeneric {
   public static class Person {
       private String name;
       private int age;        public Person(String name, int age) {
           this.name = name;
           this.age = age;
      }        @Override
       public String toString() {
           return "Person{" +
                   "name='" + name + '\'' +
                   ", age=" + age +
                   '}';
      }
  }    public static void main(String[] args) {
       Gson gson = new Gson();
       List<Person> personList = new ArrayList<>();
       for (int i = 0; i < 5; i++) {
           personList.add(new Person("name" + i, 18 + i));
      }
       // Serialization
       String json = gson.toJson(personList);
       System.out.println(json);
       // Deserialization
       Type personType = new TypeToken<List<Person>>() {}.getType();
       List<Person> personList2 = gson.fromJson(json, personType);
       System.out.println(personList2);
  }
}

发哥讲

如果你觉得文章还不错,就请点击右上角选择发送给朋友或者转发到朋友圈~

● 扫码关注公众号

20、Java 泛型的更多相关文章

  1. 浅析Java 泛型

    泛型是JavaSE5引入的一个新概念,但是这个概念在编程语言中却是很普遍的一个概念.下面,根据以下内容,我们总结下在Java中使用泛型. 泛型使用的意义 什么是泛型 泛型类 泛型方法 泛型接口 泛型擦 ...

  2. 18.Java泛型

    1.为什么需要泛型 List list1=new ArrayList(Arrays.asList(new String("string"),new Integer(20))); S ...

  3. 【Java心得总结四】Java泛型下——万恶的擦除

    一.万恶的擦除 我在自己总结的[Java心得总结三]Java泛型上——初识泛型这篇博文中提到了Java中对泛型擦除的问题,考虑下面代码: import java.util.*; public clas ...

  4. Java泛型学习笔记 - (七)浅析泛型中通配符的使用

    一.基本概念:在学习Java泛型的过程中, 通配符是较难理解的一部分. 主要有以下三类:1. 无边界的通配符(Unbounded Wildcards), 就是<?>, 比如List< ...

  5. 全面总结Java泛型

    本文对Java泛型进行了全面的总结.文章内容包括普通泛型.通配符.受限泛型.泛型接口.泛型方法.返回泛型类型实例等等. 虽然Scala创始人Martin Odersky说当年正是因为Java泛型的丑陋 ...

  6. Java学习笔记(二一)——Java 泛型

    [前面的话] 最近脸好干,掉皮,需要买点化妆品了. Java泛型好好学习一下. [定义] 一.泛型的定义主要有以下两种: 在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个 ...

  7. Java 泛型(Generics)

    Generics, 类似C++中的模版. 允许在定义类和接口的时候使用类型参数(type parameters), 声明的类型参数在使用的时候用具体的类型来替换. 如 ArrayList<Str ...

  8. java泛型 8 泛型的内部原理:类型擦除以及类型擦除带来的问题

    参考:java核心技术 一.Java泛型的实现方法:类型擦除 前面已经说了,Java的泛型是伪泛型.为什么说Java的泛型是伪泛型呢?因为,在编译期间,所有的泛型信息都会被擦除掉.正确理解泛型概念的首 ...

  9. Java泛型介绍!!!

    Java总结篇系列:Java泛型  转自:http://www.cnblogs.com/lwbqqyumidi/p/3837629.html 一. 泛型概念的提出(为什么需要泛型)? 首先,我们看下下 ...

随机推荐

  1. EM算法的收敛性

    https://blog.csdn.net/kevinoop/article/details/80522477

  2. echarts 实战 : 恼人的间隔问题

    使用 echarts 的时候,可能我们需要这个图表的间隔是固定的.比如 3个 4个 5个. (注意计算间隔数量的时候是不算 x轴 本身的.) 这个问题看似简单,其实有点麻烦. yAxis.splitN ...

  3. SW算法求全局最小割(Stoer-Wagner算法)

    我找到的唯一能看懂的题解:[ZZ]最小割集Stoer-Wagner算法 似乎是一个冷门算法,连oi-wiki上都没有,不过洛谷上竟然有它的模板题,并且2017百度之星的资格赛还考到了.于是来学习一下. ...

  4. CentOS7编译安装php7.1配置教程详解

    这篇文章主要介绍CentOS7编译安装php7.1的过程和配置详解,亲测 ,需要的朋友可以参考. 1.首先安装依赖包: yum install libxml2 libxml2-devel openss ...

  5. CODING DevOps + Nginx-ingress 实现自动化灰度发布

    作者:王炜,CODING DevOps 后端开发工程师,拥有多年研发经验,云原生.DevOps.Kubernetes 资深爱好者,Servicemesher 服务网格中文社区成员.获得 Kuberne ...

  6. APP自动化 -- 获取toast元素的文本内容

    一.toast元素 1.表现形式:toast元素就是下图中  “操作成功” 那个一闪而过的标签. 2.特殊点:因为一闪而过,时间太短,用UIAutomatorView截屏截不到. 二.获取方法 1.用 ...

  7. 16 . Go之网络编程

    互联网的本质 两台计算机之间的通信与两个人打电话原理是一样的. # 1. 首先要通过各种物理连接介质连接 # 2. 找准确对方计算机(准确到软件)的位置 # 3. 通过统一的标准(一般子协议)进行数据 ...

  8. sqlite 显示表内容时乱码,无法正常显示汉字,

    把txt文件另存为时,选择编码为utf-8即可

  9. 初学Vue.js,用 vue ui 创建项目会不会被鄙视

    全栈的自我修养: 6使用vue ui进行vue.js环境搭建 It is only with the heart that one can see rightly. What is essential ...

  10. 使用MacOS自带的SVN客户端

    原文链接:https://jingyan.baidu.com/article/5552ef479c1554518ffbc92f.html 摘要:mac环境下有自带的SVN服务端和客户端,SVN是许多公 ...