java泛型基本知识及通用方法

泛型的基本使用

泛型是Java SE 1.5的新特性, 泛型的本质是参数化类型, 也就是说所操作的数据类型被指定为一个参数. 这种参数类型可以用在类、接口和方法的创建中, 分别称为泛型类、泛型接口、泛型方法.  Java语言引入泛型的好处是安全简单.

今天就从以下几个方面介绍一下java的泛型: 基础, 泛型关键字, 泛型方法, 泛型类和接口.

基础:

  通过集合的泛型了解泛型的基本使用

public void testBasis(){
 List<String> list = new ArrayList<String>();
// new ArrayList<int>();
 }

 //这是最基本的泛型使用, 就不多说了, 不过要注意的是泛型只能是引用数据类型, 不能是基本类型, 而且泛型只在编译期有效, 在编译后的class文件中是不存在泛型信息的

注意: 泛型只在编译期有效, 在编译后的class文件中是不存在泛型信息的

泛型关键字:

  通配符?表示任意引用类型, extends关键字表示子类及其本身, super关键字是表示父类及其本身. 通过一个例子看一下, 解释说明都在例子中

public void testKeyWord() throws Exception {
 //实例化参数类型必须指明具体类型
 List<?> list = new ArrayList<String>();
 //由于list中类型不明确, 所以不能进行添加操作
// list.add("sk");

 //通配符?表示任意引用类型
 List<List<?>> list1 = new ArrayList<List<?>>();
 //list1的参数化类型是一个List, 而这个List也是一个参数化类型, 它的类型是任意类型, 所以list1可以添加任意List类型对象
 list1.add(new ArrayList<Object>());
 list1.add(new ArrayList<String>());

 //实例化list2时指明了类型参数List, 只不过这个List是一个泛型类型
 //从这里可以看到关键字extends的用法, 其实就是继承, 如下这里的list2中的参数化类型List(在这里记为paramList)的参数化类型是继承自Number的
 //所以在list2在添加的时候只能添加参数化类型为Number及其子类的paramList
 List<Integer> l1 = new ArrayList<Integer>();
 List<Number> l2 = new ArrayList<Number>();
 List<Object> l3 = new ArrayList<Object>();
 List<List<? extends Number>> list2 = new ArrayList<List<? extends Number>>();
 list2.add(l1);
 list2.add(l2);
// list2.add(l3); //这里使用了extends关键字, 所以不能添加Number的父类

 //相信大家也猜到了, 既然extends关键字表示子类及其本身, 那么super关键字是表示父类及其本身, 是的, 没错
 //和上面的不一样了, l1不能添加, 因为Integer是Number的子类, 并不是Number的父类
 List<List<? super Number>> list3 = new ArrayList<List<? super Number>>();
// list3.add(l1); //这里使用了super关键字, 所以不能添加Number的子类
 list3.add(l2);
 list3.add(l3);
 }

泛型方法:

  java中任何类型必须先定义才能使用, 泛型也是如此. 既然要使用泛型作为参数, 所以要先定义, 泛型的定义在访问修饰符和返回类型之间, 注意不要掉了尖括号

//无返回值有参的方法, 参数为泛型
 public <T> void show(T t){
 System.out.println("t-=-=" + t);
 }

 //有返回值的有参方法, 只有一个参数化类型, 这里定义泛型的方式和上面一样, 也是先定义后使用, 只不过这里的返回类型是泛型
 public <T> T get(T t){
 return t;
 }

 //有返回值的有参方法, 有多个参数化类型, 这里以两个为例
 public <T, K> K get(T t, K k){
 return k;
 }

 @Test
 public void testGeneric() throws Exception {
 show(3);
 show("generic");
 System.out.println("----------------");

 System.out.println("我返回Integer类型-" + get(4));
 System.out.println("我返回String类型-" + get("returnGeneric"));
 System.out.println("------------------");

 System.out.println("我返回String类型-" + get(1, "a"));
 System.out.println("我返回Integer类型-" + get("b", 2));
 }

  本来想写无参的泛型方法, 可是写着写着感觉那样没有什么意义, 不知道各位有什么见解.

泛型类和接口:

  写泛型类的时候只需要在类名后面加上泛型即可, 就像这样

public class GenericClass<T> {
 public T get(T t){
  return t;
 }

 public void scr(T t){
  System.out.println(t);
 }

 public void show(){
  GenericClass<Integer> gc = new GenericClass<Integer>();
//  GenericClass<T> gc1 = new GenericClass<T>();
  gc.get(3);
  gc.scr(5);
  //下面2个会报错
//  gc1.get(3);
//  gc1.scr(5);
 }
}

  从上面的例子中可以看到, 参数化类型是在创建对象的时候具体化的, 那么除此之外, 还可以再什么时候具体化参数化类型呢?

  如果是在继承或实现中, 可以在子类或实现类中确定具体类型

使用java泛型设计通用方法

泛型是Java SE 1.5的新特性, 泛型的本质是参数化类型, 也就是说所操作的数据类型被指定为一个参数. 因此我们可以利用泛型和反射来设计一些通用方法. 现在有2张表, 一张user表和一张student表.

user:

student:

  如果要根据id查询数据, 你会怎么做呢?写2个方法分别查询user和student?其实这时候我们就可以使用泛型和反射写一个通用的方法.

  user的实体类:

private Integer id;
 private String username;
 private String password;
 private String hobby;
 //getXxx方法和setXxx方法

  student实体类:

 private Integer id;
 private String name;
 private Integer age;
 //getXxx方法和setXxx方法

 BaseDao接口:

public interface BaseDao<T> {
 //根据id查询的方法
 T findById(Integer id);
}

  BaseDaoImpl类, 实现了BaseDao接口, 通用方法就在这里面完成:

//设置为抽象的, 不让他实例化, 让其子类实例化就行了
//通过泛型设计通用方法的关键就是利用反射拿到泛型的具体类型
public abstract class BaseDaoImpl<T> implements BaseDao<T> {
 private String tableName;  //表名
 private Class<T> actualType;//真实类型

 /**
 * findById(Integer id)这个方法被子类继承了, 假设我们在UserDaoImpl中操作, 此时参数化类型T为User
 * 下面的讲解都假设是在UserDaoImpl中进行的
 */
 //把公共部分可以放到构造方法中
 @SuppressWarnings("unchecked")
 public BaseDaoImpl() {
 //返回类型是Type 是 Java 编程语言中所有类型的公共高级接口. 它们包括原始类型、参数化类型、数组类型、类型变量和基本类型.
 //Type的已知子接口: ParameterizedType 表示参数化类型, 如 Collection<String>.
 //getClass()得到UserDaoImpl的Class, 得到Class一般有3中方式: getClass(), 类名.class, Class.forName()
 Type type = getClass().getGenericSuperclass();//获取UserDaoImpl<User>的参数化类型的父类BaseDaoImpl<User>
 //由于我们得到的是一个参数化类型, 所以转成ParameterizedType, 因为需要使用里面的方法
 ParameterizedType pt = (ParameterizedType) type;//强转
 Type[] actualTypeArr = pt.getActualTypeArguments();//获取真实参数类型数组[User.class], 可以调试看到这里的值
 actualType = (Class<T>) actualTypeArr[0];//数组只有一个元素
 tableName = actualType.getSimpleName();
 }

 @Override
 public T findById(Integer id) {
 String sql = "select * from " + tableName + " where id = ?";
 try {
 return QRUtils.getQueryRunner().query(sql, new BeanHandler<T>(actualType), id);
 } catch (SQLException e) {
 e.printStackTrace();
 }
 return null;
 }
}

  连接数据库操作是用的c3p0和dbutils, 有关这方面的内容可以参看我以前的随笔.     

  UserDao接口, 继承BaseDao接口:

public interface UserDao<T> extends BaseDao<T> {
}

  UserDaoImpl类, 实现UserDao接口, 继承BaseDaoImpl类, 可以看到里面什么方法也没有:

public class UserDaoImpl extends BaseDaoImpl<User> implements UserDao<User> {
}

  StudentDao接口, 继承BaseDao接口:

public interface StudentDao<T> extends BaseDao<T> {
}

  StudentDaoImpl类, 实现StudentDao接口, 继承BaseDaoImpl类, 也可以看到里面什么方法也没有:

public class StudentDaoImpl extends BaseDaoImpl<Student> implements StudentDao<Student> {
}

  从以上dao可以看到, 我是在继承BaseDaoImpl类时把泛型具体化了.

测试:

@Test
  public void testGeneric() throws Exception {
  UserDao<User> userDao = new UserDaoImpl();
  System.out.println(userDao.findById(1));

  System.out.println("-------------------");
  StudentDao<Student> studentDao = new StudentDaoImpl();
  System.out.println(studentDao.findById(1));
 }

  看一下测试的结果: 同一个方法把2张表中的数据都查出来了

  

(0)

相关推荐

  • Java构造函数的相互调用代码示例

    在Java中,当为一个类创建了多个构造函数时,有时想在一个构造函数中调用另一个构造函数以减少代码量.这时可以使用this关键字来实现. 有关构造函数的相关内容,大家可以参阅:Java编程中的构造函数详细介绍 通常,当使用this关键字时,它意味着"这个对象"或者"当前对象",并且它自身产生对当前对象的引用.在一个构造函数中,当给传递给它一个参数列表时,它就有了不同的意义. 它将直接的调用能够匹配这个参数列表的构造函数.因此,我么可以直接的调用其它构造函数: pack

  • Java构造函数与普通函数用法详解

    函数也被称为方法! 函数的作用及特点: 1.用于定义功能,将功能封装. 2.可以提高代码的复用性. 函数注意事项: 1.不能进行函数套用(不可以在函数内定义函数). 2.函数只有被调用才能被执行. 3.基本数据类型(String.int.-.)修饰的函数类型,要有return返回值. 4.void修饰的函数,函数中的return语句可以省略不写. 5.函数名可以根据需求进行命名. 代码示例:(有无函数/方法的区别) 无函数/方法代码例子: public class NoFunc { public

  • 深入理解Java中的构造函数引用和方法引用

    JDK 8 见证了一个特殊特性的出现:构造函数引用和方法引用.在本文中, Adrian D. Finlay 探讨了开发人员如何释放构造函数引用的真正潜力. 方法引用的一些背景 如果你还不知道 Java 构造函数本身就是特殊的方法,那么阅读方法引用的基本示例将对读者有所帮助,通过了解这些内容,可以了解构造函数引用是什么. 「方法引用为已经有名称的方法提供易读的 lambda 表达式.」 「它们提供了一种无需执行就可以引用方法的简单方式.」 以上引自<Java 8 编程参考官方教程(第 9 版)>

  • 不同Java泛型构造函数的详解

    1.概述 我们之前讨论过Java Generics的基础知识.在本文中,我们将了解Java中的通用构造函数. 泛型构造函数是至少需要有一个泛型类型参数的构造函数.我们将看到泛型构造函数并不都是在泛型类中出现的,而且并非所有泛型类中的构造函数都必须是泛型. 2.非泛型类 首先,先写一个简单的类:Entry,它不是泛型类: public class Entry { private String data; private int rank; } 在这个类中,我们将添加两个构造函数:一个带有两个参数的

  • 详解Java泛型及其应用

    引出泛型 我们通过如下的示例,引出为什么泛型的概念. public class Test { public static void main(String[] args) { List list = new ArrayList(); list.add("abc"); list.add(2); for (int i = 0; i < list.size(); i++) { String name = (String) list.get(i); // error System.out

  • 简单理解java泛型的本质(非类型擦除)

    背景 之前在网上发现这个问题 public class GenericTest { //方法一 public static <T extends Comparable<T>> List<T> sort(List<T> list) { return Arrays.asList(list.toArray((T[]) new Comparable[list.size()])); } //方法二 public static <T extends Compara

  • Java中泛型总结(推荐)

    Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型. 泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数. 泛型类 范例:泛型类的基本语法 class MyClass<T> { T value1; } 尖括号 <> 中的 T 被称作是类型参数,用于指代任何类型.实际上这个T你可以任意写,但出于规范的目的,Java还是建议我们用单个大写字母来代表类型参数.常见的如: T 代表

  • java泛型基本知识及通用方法

    泛型的基本使用 泛型是Java SE 1.5的新特性, 泛型的本质是参数化类型, 也就是说所操作的数据类型被指定为一个参数. 这种参数类型可以用在类.接口和方法的创建中, 分别称为泛型类.泛型接口.泛型方法.  Java语言引入泛型的好处是安全简单. 今天就从以下几个方面介绍一下java的泛型: 基础, 泛型关键字, 泛型方法, 泛型类和接口. 基础: 通过集合的泛型了解泛型的基本使用 public void testBasis(){ List<String> list = new Array

  • java泛型基本知识和通用方法

    一.泛型简介 1.引入泛型的目的 了解引入泛型的动机,就先从语法糖开始了解. 语法糖 语法糖(Syntactic Sugar),也称糖衣语法,是由英国计算机学家Peter.J.Landin发明的一个术语,指在计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用.Java中最常用的语法糖主要有泛型.变长参数.条件编译.自动拆装箱.内部类等.虚拟机并不支持这些语法,它们在编译阶段就被还原回了简单的基础语法结构,这个过程成为解语法糖. 泛型的目的: Java 泛型就是把一种

  • java中List对象排序通用方法

    本文实例讲述了java中List对象排序通用方法.分享给大家供大家参考.具体分析如下: 在数据库中查出来的列表list中,往往需要对不同的字段重新排序,一般的做法都是使用排序的字段,重新到数据库中查询.如果不到数据库查询,直接在第一次查出来的list中排序,无疑会提高系统的性能. 只要把第一次查出来的结果存放在session中,就可以对list重新排序了.一般对list排序可以使用Collections.sort(list),但如果list中包含是一个对象的话,这种方法还是行不通的.那要怎么排序

  • 新手了解java 泛型基础知识

    目录 1.什么是泛型 2.泛型的使用规则 3.泛型应用实例 总结 1.什么是泛型 ​ 泛型,就是允许在定义类.接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型.这个类型参数将在使用时(例 如,继承或实现这个接口,用这个类型声明变量.创建对象时)确定(即 传入实际的类型参数,也称为类型实参). 泛型是一种参数化类型. 2.泛型的使用规则 泛型是JDK5.0出现,在之前的版本中是不能使用的: 泛型是需要写在一对<>中的: 泛型的类型必须是引用类型,不能是基本数据类型: 如果

  • Java 泛型总结及详解

    一. 泛型概念的提出(为什么需要泛型)? 首先,我们看下下面这段简短的代码: public class GenericTest { public static void main(String[] args) { List list = new ArrayList(); list.add("qqyumidi"); list.add("corn"); list.add(100); for (int i = 0; i < list.size(); i++) { S

  • Java泛型最全知识总结

    一.泛型简介 1.1 泛型的概念 所谓泛型,就是允许在定义类.接口时通过一个标识表示类中某个属性的类型或者是某个方法的返 回值及参数类型.这个类型参数将在使用时(例如,继承或实现这个接口,用这个类型声明变量. 创建对象时确定(即传入实际的类型参数,也称为类型实参). 从JDK 5.0以后,Java引入了"参数化类型(Parameterized type)"的概念,允许我们在创建集合时再指定集合元素的类型,正如:List,这表明该List只能保存字符串类型的对象. JDK 5.0改写了集

  • Java泛型<T> T与T的使用方法详解

    泛型(Generic type 或者 generics)是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类.可以把类型参数看作是使用参数化类型时指定的类型的一个占位符,就像方法的形式参数是运行时传递的值的占位符一样. 在集合框架(Collection framework)中泛型的身影随处可见.例如,Map 类允许向一个 Map 类型的实例添加任意类的对象,即使最常见的情况在给定映射(map)中保存一个string键值对. 命名类型参数 对于常见的泛型模式,推荐的泛型类型

  • Java泛型机制与反射原理相关知识总结

    一.泛型的概念 1.1 基础案例 泛型在Java中的应用非常广泛,最常见则是在集合容器中,先看下基础用法: public class Generic01 { public static void main(String[] args) { Map<Integer,String> map = new HashMap<>() ; map.put(88,"hello") ; // map.put("99","world") ;

  • 浅谈Java泛型让声明方法返回子类型的方法

    泛型典型的使用场景是集合.考虑到大多数情况下集合是同质的(同一类型),通过声明参数类型,可免去类型转换的麻烦.本文将讨论本人阅读Spring Security源码时遇到的一个关于泛型递归模式的问题. 声明方法返回子类型 在Spring Security的源码里有一个ProviderManagerBuilder接口,声明如下 public interface ProviderManagerBuilder<B extends ProviderManagerBuilder<B>> ext

  • 应用Java泛型和反射导出CSV文件的方法

    本文实例讲述了应用Java泛型和反射导出CSV文件的方法.分享给大家供大家参考.具体如下: 项目中有需求要把数据导出为CSV文件,因为不同的类有不同的属性,为了代码简单,应用Java的泛型和反射,写了一个函数,完成导出功能. 复制代码 代码如下: public <T> void saveFile(List<T> list, String outFile) throws IOException {         if (list == null || list.isEmpty())

随机推荐