23.java中泛型的使用(附讲解和练习)

2023-10-17 00:38
文章标签 java 讲解 使用 练习 23 中泛

本文主要是介绍23.java中泛型的使用(附讲解和练习),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

泛型

泛型的定义

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

在集合中使用泛型

1.jdk 5.0新增的特性

2.在集合中使用泛型:
总结:
① 集合接口或集合类在jdk5.0时都修改为带泛型的结构。
② 在实例化集合类时,可以指明具体的泛型类型
③ 指明完以后,在集合类或接口中凡是定义类或接口时,内部结构(比如:方法、构造器、属性等)使用到类的泛型的位置,都指定为实例化的泛型类型。
比如:add(E e) —>实例化以后:add(Integer e)
注意点:泛型的类型必须是类,不能是基本数据类型。需要用到基本数据类型的位置,拿包装类替换
⑤ 如果实例化时,没有指明泛型的类型。默认类型为java.lang.Object类型。

3.如何自定义泛型结构:泛型类、泛型接口;泛型方法。见 GenericTest1.java

下面展示一些 在集合中使用泛型

public class GenericTest {//在集合中使用泛型之前的情况:@Testpublic void test1(){ArrayList list = new ArrayList();//需求:存放学生的成绩list.add(78);list.add(76);list.add(89);list.add(88);//问题一:类型不安全
//        list.add("Tom");for(Object score : list){//问题二:强转时,可能出现ClassCastExceptionint stuScore = (Integer) score;System.out.println(stuScore);}}//在集合中使用泛型的情况:以ArrayList为例@Testpublic void test2(){ArrayList<Integer> list =  new ArrayList<Integer>();list.add(78);list.add(87);list.add(99);list.add(65);//编译时,就会进行类型检查,保证数据的安全
//        list.add("Tom");//方式一:
//        for(Integer score : list){
//            //避免了强转操作
//            int stuScore = score;
//
//            System.out.println(stuScore);
//
//        }//方式二:Iterator<Integer> iterator = list.iterator();while(iterator.hasNext()){int stuScore = iterator.next();System.out.println(stuScore);}}//在集合中使用泛型的情况:以HashMap为例@Testpublic void test3(){
//        Map<String,Integer> map = new HashMap<String,Integer>();//jdk7新特性:类型推断Map<String,Integer> map = new HashMap<>();map.put("Tom",87);map.put("Jerry",87);map.put("Jack",67);//        map.put(123,"ABC");//泛型的嵌套Set<Map.Entry<String,Integer>> entry = map.entrySet();Iterator<Map.Entry<String, Integer>> iterator = entry.iterator();while(iterator.hasNext()){Map.Entry<String, Integer> e = iterator.next();String key = e.getKey();Integer value = e.getValue();System.out.println(key + "----" + value);}}}

自定义泛型结构

自定义泛型结构:泛型类、泛型接口

  1. 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如:<E1,E2,E3>
  2. 泛型类的构造器如下: public GenericClass(){}。而下面是错误的: public GenericClass(){}
  3. 实例化后,操作原来泛型位置的结构必须与指定的泛型类型一致。
  4. 泛型不同的引用不能相互赋值。尽管在编译时ArrayList和ArrayList是两种类型,但是,在运行时只有一个ArrayList被加载到JVM中。
  5. 泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。 经验: 泛型要使用一路都用。要不用,一路都不要用。
  6. 如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象。
  7. jdk1.7,泛型的简化操作: ArrayList flist = new ArrayList<>();
  8. 泛型的指定中不能使用基本数据类型,可以使用包装类替换。
  9. 在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法中不能使用类的泛型。
  10. 异常类不能是泛型的
  11. 不能使用new E[]。但是可以: E[] elements = (E[])new Object[capacity]; 参考: ArrayList源码中声明: Object[] elementData, 而非泛型参数类型数组。
    12.父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型:
    》子类不保留父类的泛型:按需实现
     没有类型 擦除
     具体类型
    》子类保留父类的泛型:泛型子类
     全部保留
     部分保留
    结论:子类必须是“富二代”,子类除了指定或保留父类的泛型,还可以增加自己的泛型

自定义泛型结构:泛型方法
 方法,也可以被泛型化,不管此时定义在其中的类是不是泛型类。 在泛型方法中可以定义泛型参数,此时,参数的类型就是传入数据的类型。
 泛型方法的格式:
[访问权限] <泛型> 返回类型 方法名([泛型标识 参数名称]) 抛出的异常
 泛型方法声明泛型时也可以指定上限(在12.5中讲)

下面展示一些 泛型类、泛型接口、泛型方法的调用

public class GenericTest1 {@Testpublic void test1(){//如果定义了泛型类,实例化没有指明类的泛型,则认为此泛型类型为Object类型//要求:如果大家定义了类是带泛型的,建议在实例化时要指明类的泛型。Order order = new Order();order.setOrderT(123);order.setOrderT("ABC");//建议:实例化时指明类的泛型Order<String> order1 = new Order<String>("orderAA",1001,"order:AA");order1.setOrderT("AA:hello");}@Testpublic void test2(){SubOrder sub1 = new SubOrder();//由于子类在继承带泛型的父类时,指明了泛型类型。则实例化子类对象时,不再需要指明泛型。sub1.setOrderT(1122);SubOrder1<String> sub2 = new SubOrder1<>();sub2.setOrderT("order2...");}@Testpublic void test3(){ArrayList<String> list1 = null;ArrayList<Integer> list2 = new ArrayList<Integer>();//泛型不同的引用不能相互赋值。
//        list1 = list2;Person p1 = null;Person p2 = null;p1 = p2;}//测试泛型方法@Testpublic void test4(){Order<String> order = new Order<>();Integer[] arr = new Integer[]{1,2,3,4};//泛型方法在调用时,指明泛型参数的类型。List<Integer> list = order.copyFromArrayToList(arr);System.out.println(list);}
}

下面展示一些 自定义泛型结构:泛型类、泛型接口、泛型方法

public class Order<T> {String orderName;int orderId;//类的内部结构就可以使用类的泛型T orderT;public Order(){//编译不通过
//        T[] arr = new T[10];//编译通过T[] arr = (T[]) new Object[10];}public Order(String orderName,int orderId,T orderT){this.orderName = orderName;this.orderId = orderId;this.orderT = orderT;}//如下的三个方法都不是泛型方法public T getOrderT(){return orderT;}public void setOrderT(T orderT){this.orderT = orderT;}@Overridepublic String toString() {return "Order{" +"orderName='" + orderName + '\'' +", orderId=" + orderId +", orderT=" + orderT +'}';}//静态方法中不能使用类的泛型。
//    public static void show(T orderT){
//        System.out.println(orderT);
//    }public void show(){//编译不通过
//        try{
//
//
//        }catch(T t){
//
//        }}//泛型方法:在方法中出现了泛型的结构,泛型参数与类的泛型参数没有任何关系。//换句话说,泛型方法所属的类是不是泛型类都没有关系。//泛型方法,可以声明为静态的。原因:泛型参数是在调用方法时确定的。并非在实例化类时确定。public static <E>  List<E> copyFromArrayToList(E[] arr){ArrayList<E> list = new ArrayList<>();for(E e : arr){list.add(e);}return list;}
}

泛型在继承上的体现

如果B是A的一个子类型(子类或者子接口),而G是具有泛型声明的类或接口, G并不是G的子类型!
比如: String是Object的子类,但是List并不是List的子类。
在这里插入图片描述
下面展示一些 泛型在继承上的调用

public class GenericTest {/*1. 泛型在继承方面的体现虽然类A是类B的父类,但是G<A> 和G<B>二者不具备子父类关系,二者是并列关系。补充:类A是类B的父类,A<G> 是 B<G> 的父类*/@Testpublic void test1(){Object obj = null;String str = null;obj = str;Object[] arr1 = null;String[] arr2 = null;arr1 = arr2;//编译不通过
//        Date date = new Date();
//        str = date;List<Object> list1 = null;List<String> list2 = new ArrayList<String>();//此时的list1和list2的类型不具有子父类关系//编译不通过
//        list1 = list2;/*反证法:假设list1 = list2;list1.add(123);导致混入非String的数据。出错。*/show(list1);show1(list2);}public void show1(List<String> list){}public void show(List<Object> list){}@Testpublic void test2(){AbstractList<String> list1 = null;List<String> list2 = null;ArrayList<String> list3 = null;list1 = list3;list2 = list3;List<String> list4 = new ArrayList<>();}}

通配符的使用

通配符的使用
1.使用类型通配符:
比如: List<?> , Map<?,?>
List<?>是List、 List等各种泛型List的父类。
2.读取List<?>的对象list中的元素时,永远是安全的,因为不管list的真实类型是什么,它包含的都是Object。
3.写入list中的元素时,不行。因为我们不知道c的元素类型,我们不能向其中添加对象。
 唯一的例外是null,它是所有类型的成员。
 将任意元素加入到其中不是类型安全的:
Collection<?> c = new ArrayList();
c.add(new Object()); // 编译时错误
因为我们不知道c的元素类型,我们不能向其中添加对象。 add方法有类型参数E作为集合的元素类型。我们传给add的任何参数都必须是一个未知类型的子类。因为我们不知道那是什么类型,所以我们无法传任何东西进去。
 唯一的例外的是null,它是所有类型的成员。
 另一方面,我们可以调用get()方法并使用其返回值。返回值是一个未知的类型,但是我们知道,它总是一个Object。

有限制的通配符
 <?>
允许所有泛型的引用调用
 通配符指定上限
上限extends:使用时指定的类型必须是继承某个类,或者实现某个接口,即<=
 通配符指定下限
下限super:使用时指定的类型不能小于操作的类,即>=
 举例:

  • <? extends Number> (无穷小 , Number], 只允许泛型为Number及Number子类的引用调用
  • <? super Number> [Number , 无穷大),只允许泛型为Number及Number父类的引用调用
  • <? extends Comparable>只允许泛型为实现Comparable接口的实现类的引用调用

下面展示一些 通配符的使用及限制

  /*2. 通配符的使用通配符:?类A是类B的父类,G<A>和G<B>是没有关系的,二者共同的父类是:G<?>*/@Testpublic void test3(){List<Object> list1 = null;List<String> list2 = null;List<?> list = null;list = list1;list = list2;//编译通过
//        print(list1);
//        print(list2);//List<String> list3 = new ArrayList<>();list3.add("AA");list3.add("BB");list3.add("CC");list = list3;//添加(写入):对于List<?>就不能向其内部添加数据。//除了添加null之外。
//        list.add("DD");
//        list.add('?');list.add(null);//获取(读取):允许读取数据,读取的数据类型为Object。Object o = list.get(0);System.out.println(o);}public void print(List<?> list){Iterator<?> iterator = list.iterator();while(iterator.hasNext()){Object obj = iterator.next();System.out.println(obj);}}/*3.有限制条件的通配符的使用。? extends A:G<? extends A> 可以作为G<A>和G<B>的父类,其中B是A的子类? super A:G<? super A> 可以作为G<A>和G<B>的父类,其中B是A的父类*/@Testpublic void test4(){//相当于[x<= PersonList<? extends Person> list1 = null;//相当于x >= PersonList<? super Person> list2 = null;List<Student> list3 = new ArrayList<Student>();List<Person> list4 = new ArrayList<Person>();List<Object> list5 = new ArrayList<Object>();list1 = list3;list1 = list4;
//        list1 = list5;//        list2 = list3;list2 = list4;list2 = list5;//读取数据:list1 = list3;Person p = list1.get(0);//编译不通过//Student s = list1.get(0);list2 = list4;Object obj = list2.get(0);编译不通过
//        Person obj = list2.get(0);//写入数据://编译不通过
//        list1.add(new Student());//编译通过list2.add(new Person());list2.add(new Student());}

下面展示一些 父类

public class Person {
}

下面展示一些 子类继承类

public class Student extends Person {
}

泛型应用举例

下面展示一些 泛型应用举例1

public class DAO<T> {private Map<String,T> map = new HashMap<String,T>();//保存 T 类型的对象到 Map 成员变量中public void save(String id,T entity){map.put(id,entity);}//从 map 中获取 id 对应的对象public T get(String id){return map.get(id);}//替换 map 中key为id的内容,改为 entity 对象public void update(String id,T entity){if(map.containsKey(id)){map.put(id,entity);}}//返回 map 中存放的所有 T 对象public List<T> list(){//错误的:
//        Collection<T> values = map.values();
//        return (List<T>) values;//正确的:ArrayList<T> list = new ArrayList<>();Collection<T> values = map.values();for(T t : values){list.add(t);}return list;}//删除指定 id 对象public void delete(String id){map.remove(id);}}

下面展示一些 泛型应用举例2

public class User {private int id;private int age;private String name;public int getId() {return id;}public void setId(int id) {this.id = id;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public User(int id, int age, String name) {this.id = id;this.age = age;this.name = name;}public User() {}@Overridepublic String toString() {return "User{" +"id=" + id +", age=" + age +", name='" + name + '\'' +'}';}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;User user = (User) o;if (id != user.id) return false;if (age != user.age) return false;return name != null ? name.equals(user.name) : user.name == null;}@Overridepublic int hashCode() {int result = id;result = 31 * result + age;result = 31 * result + (name != null ? name.hashCode() : 0);return result;}
}

下面展示一些 泛型应用举例3

public class DAOTest {public static void main(String[] args) {DAO<User> dao = new DAO<User>();dao.save("1001",new User(1001,34,"周杰伦"));dao.save("1002",new User(1002,20,"昆凌"));dao.save("1003",new User(1003,25,"蔡依林"));dao.update("1003",new User(1003,30,"方文山"));dao.delete("1002");List<User> list = dao.list();
//        System.out.println(list);list.forEach(System.out::println);}}

这篇关于23.java中泛型的使用(附讲解和练习)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/221779

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数