百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

三石说:java 基础 之 泛型

lipiwang 2024-11-22 17:21 4 浏览 0 评论

在没有介绍泛型的使用时想想我们在哪些地方使用泛型的居多,那么一定是集合中。在前面的集合中已经看到了使用到泛型了,今天来看下泛型具体的概念及用法。

泛型:

要使代码能够应用于“某种具体的类型而不是一个具体的接口或类”。增加了泛型支持后的集合,完全可以记住集合中元素的类型,并可以在编译时检查集合中元素的类型,如果试图向集合中添加不满足类型要求的对象,编译器就会提示错误。增加泛型后的集合,可以让代码更加简洁,程序更加健壮(Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常)。除此之外,Java泛型还增强了枚举类、反射等方面的功能

当创建带泛型声明的自定义类,为该类定义构造器时,构造器名还是原来的类名,不要增加泛型声明。

例如,为Apple类定义构造器,其构造器名依然是Apple,而不是Apple<T>调用该构造器时却可以使用Apple的形式,当然应该为T形参传入实际的类型参数。Java 7提供了菱形语法,允许省略<>中的类型实参。

元组:

将一组对象直接打包存储于其中的一个单一的对象。这个容器对象允许读取其中的元素,但是不允许对其存放新的对象(这也称为:数据传送对象)

比如一个二维的元组:

public class TwoTuple<A,B>{
    public final A first;
    public final B second;
    public TwoTuple(A a, B b){ first = a; second = b;} 
    public String toStirng() {
        return "("+first+","+second+")";
    }
}

用泛型类构造一个堆栈类:

下面的例子使用了末端哨兵来判断堆栈何时为空,这个末端哨兵在构造LinkedStack时创建,然后每调用一次push()方法,就会创建一个Node对象,并将其链接到前一个Node对象。

public class LinkedStack<T> {
  private static class Node<U> {
    U item;
    Node<U> next;
    Node() { item = null; next = null; }
    Node(U item, Node<U> next) {
      this.item = item;
      this.next = next;
    }
    boolean end() { return item == null && next == null; }
  }
  private Node<T> top = new Node<T>(); // End sentinel
  public void push(T item) {
    top = new Node<T>(item, top);
  }    
  public T pop() {
    T result = top.item;
    if(!top.end())
      top = top.next;
    return result;
  }
  public static void main(String[] args) {
    LinkedStack<String> lss = new LinkedStack<String>();
    for(String s : "Phasers on stun!".split(" "))
      lss.push(s);
    String s;
    while((s = lss.pop()) != null)
      System.out.println(s);
  }
} /* Output:
stun!
on
Phasers
*///:~

泛型接口:

public interface Generator{T next();}
用泛型接口实现生成Fibonacci数列:

public class Fibonacci implements Generator<Interger> {
    private int count=0;
    public Integer next(){return fib(count++);}
    private int fib(int n){
        if(n<2) return 1;
        return fib(n-2)+fib(n-1);
    }
    public static void main(String[] args){
        Fibonacci gen = new Fibonacci();
        for(int i=0;i<18;i++){
            System.out.println(gen.next()+" ");
        }
    }
}
/* output
1 1 2 3 5 8 13 21 34 55 89 144 233 377 ...

泛型方法:

泛型方法使得该方法能够独立于类而产生变化。无论何时,只要你能做到,就应该尽量使用泛型方法。也就是说如果使用泛型方法可以取代整个类泛型化,那么就应该使用泛型方法,因为他可以使得事情更清楚明白。

定义泛型方法:修饰符 返回参数 方法名(接收参数){}

public class MyUtils{


泛型中的类型转换,在未知类型时不能进行强转:用下面方式应该先判断
List<?>[] lsa=new ArrayList<?>[10];
Object[] oa=(Object[]) lsa;
List<Integer> li=new ArrayList<Integer>();
li.add(new Integer(3));
oa[1]=li;
Object target=lsa[1].get(0);
if (target instanceof String){
// 下面代码安全了
String s=(String) target;
}
使用泛型构建一个Set的使用工具(交集,差集):
import java.util.*;
public class Sets {
  // 将两个set合并为一个set  
  public static <T> Set<T> union(Set<T> a, Set<T> b) {
    Set<T> result = new HashSet<T>(a);
    result.addAll(b);
    return result;
  }
  // 返回共有的交集
  public static <T>
  Set<T> intersection(Set<T> a, Set<T> b) {
    Set<T> result = new HashSet<T>(a);
    result.retainAll(b);
    return result;
  }    
  // 从superset移除subset包含的元素  
  // Subtract subset from superset:
  public static <T> Set<T>
  difference(Set<T> superset, Set<T> subset) {
    Set<T> result = new HashSet<T>(superset);
    result.removeAll(subset);
    return result;
  }
  // 返回交集之外的所有元素
  // Reflexive--everything not in the intersection:
  public static <T> Set<T> complement(Set<T> a, Set<T> b) {
    return difference(union(a, b), intersection(a, b));
  }
} ///:~

泛型边界通配符

泛型的上限:?extends 类型。

泛型的下限:?super 类型。

如果要从集合中读取类型T的数据,并且不能写入 可以使用 ? extends 通配符;(Producer Extends)

如果要从集合中写入类型T的数据,并且不需要读取,可以使用 ? super 通配符;(Consumer Super)

// <T> 标明是一个泛型方法。
public static <T> void copy(Collection<T> dest , Collection<? extends T> src){..
    .} //①这里的?只能为T的子类 带有子类限定的可以从泛型读取
//
public static <T> T copy(Collection<? super T> dest , Collection<T> src){.
    ..} //② ?只能为T的父类或者T 带有子类限定的可以从泛型写入
}如果既要存又要取,那么就不要使用任何通配符

上界的list只能get不能add,下届的list只能add不能get

编译器可以支持像上转型,不支持像下转型。

PECS参考:https://blog.51cto.com/flyingcat2013/1616068


泛型擦除:

在泛型代码内部,无法获取任何有关泛型参数类型的信息。java使用泛型擦除,比如List 和List 在运行时都是相同的类型,均被擦除为"原生"类型即List. 泛型擦除就是被擦除为父类。保留了类型的上限

比如:

List<String> list = new ArrayList<>();
//类型被擦除了,保留的是类型的上限,String的上限就是Object
List list1 = list;


泛型的面试题

Q&A Java中的泛型是什么 ? 使用泛型的好处是什么?

在集合中存储对象并在使用前进行类型转换是多么的不方便。泛型防止了那种情况的发生。它提供了编译期的类型安全,确保你只能把正确类型的对象放入 集合中,避免了在运行时出现ClassCastException

Q&A Java的泛型是如何工作的 ? 什么是类型擦除 ?

泛型是通过类型擦除来实现的,编译器在编译时擦除了所有类型相关的信息,所以在运行时不存在任何类型相关的信息。例如 List在运行时仅用一个List来表示。这样做的目的,是确保能和Java 5之前的版本开发二进制类库进行兼容。你无法在运行时访问到类型参数,因为编译器已经把泛型类型转换成了原始类型。

Q&A什么是泛型中的限定通配符和非限定通配符 ?

限定通配符对类型进行了限制。有两种限定通配符,一种是它通过确保类型必须是T的子类来设定类型的上界,另一种是它通过确保类型必须是T的父类来设定类型的下界。泛型类型必须用限定内的类型来进行初始化,否则会导致编译错误。另一方面表 示了非限定通配符,因为可以用任意类型来替代

Q&A List 上界 和List 下界 之间有什么区别 ?

上界的list只能get不能add,下届的list只能add不能get

编译器可以支持像上转型,不支持像下转型。

Q&A 你可以把List传递给一个接受List参数的方法吗?

因为List可以存储任何类型的对象包括String, Integer等等,而List却只能用来存储Strings。

List objectList;
List stringList;
objectList = stringList; //compilation error incompatible types
public static void main(String[] args) {
        List<String> stringList = new ArrayList<>();
        List<Object> objectList = new ArrayList<>();
        stringList.add("add");
        stringList.add("123");
        objectList.add("123");
        objectList.add("234");
        // 让objectList转为stringList,编译错误stringList必须是接收的List<String> List<Object>之间不能转换。
        // stringList= objectList; // 编译错误,List<String> List<Object> 是没有任何关系的。
        // objectList=stringList; // 编译错误
        List<?> list = new ArrayList<>();
        list = stringList;
        System.out.println(list);
        list = objectList;
        System.out.println(list);
        // list.add("sss"); // 编译器不允许这样使用
    }


在泛型的面试中很大一部分会出现泛型边界问题,以及泛型的擦除。这是常常会问及的,所以我们需要格外的注意。

相关推荐

ubuntu单机安装open-falcon极度详细操作

备注:以下操作均由本人实际操作并得到验证,喜欢的同学可尝试操作安装。步骤一1.1环境准备(使用系统:ubuntu18.04)1.1.1安装redisubuntu下安装(参考借鉴:https://...

Linux搭建promtail、loki、grafana轻量日志监控系统

一:简介日志监控告警系统,较为主流的是ELK(Elasticsearch、Logstash和Kibana核心套件构成),虽然优点是功能丰富,允许复杂的操作。但是,这些方案往往规模复杂,资源占用高,...

一文搞懂,WAF阻止恶意攻击的8种方法

WAF(Web应用程序防火墙)是应用程序和互联网流量之间的第一道防线,它监视和过滤Internet流量以阻止不良流量和恶意请求,WAF是确保Web服务的可用性和完整性的重要安全解决方案。它...

14配置appvolume(ios14.6配置文件)

使用AppVolumes应用程序功能,您可以管理应用程序的整个生命周期,包括打包、更新和停用应用程序。您还可以自定义应用程序分配,以向最终用户提供应用程序的特定版本14.1安装appvolume...

目前流行的缺陷管理工具(缺陷管理方式存在的优缺点)

摘自:https://blog.csdn.net/jasonteststudy/article/details/7090127?utm_medium=distribute.pc_relevant.no...

开源数字货币交易所开发学习笔记(2)——SpringCloud

前言码云(Gitee)上开源数字货币交易所源码CoinExchange的整体架构用了SpringCloud,对于经验丰富的Java程序员来说,可能很简单,但是对于我这种入门级程序员,还是有学习的必要的...

开发JAX-RPC Web Services for WebSphere(下)

在开发JAX-RPCWebServicesforWebSphere(上)一文中,小编为大家介绍了如何创建一个Web服务项目、如何创建一个服务类和Web服务,以及部署项目等内容。接下来小编将为大...

CXF学习笔记1(cxf client)

webservice是发布服务的简单并实用的一种技术了,个人学习了CXF这个框架,也比较简单,发布了一些笔记,希望对笔友收藏并有些作用哦1.什么是webServicewebService让一个程序可...

分布式RPC最全详解(图文全面总结)

分布式通信RPC是非常重要的分布式系统组件,大厂经常考察的Dubbo等RPC框架,下面我就全面来详解分布式通信RPC@mikechen本篇已收于mikechen原创超30万字《阿里架构师进阶专题合集》...

Oracle WebLogic远程命令执行0day漏洞(CVE-2019-2725补丁绕过)预警

概述近日,奇安信天眼与安服团队通过数据监控发现,野外出现OracleWebLogic远程命令执行漏洞最新利用代码,此攻击利用绕过了厂商今年4月底所发布的最新安全补丁(CVE-2019-2725)。由...

Spring IoC Container 原理解析(spring中ioc三种实现原理)

IoC、DI基础概念关于IoC和DI大家都不陌生,我们直接上martinfowler的原文,里面已经有DI的例子和spring的使用示例《InversionofControlContainer...

Arthas线上服务器问题排查(arthas部署)

1Arthas(阿尔萨斯)能为你做什么?这个类从哪个jar包加载的?为什么会报各种类相关的Exception?我改的代码为什么没有执行到?难道是我没commit?分支搞错了?遇到问题无法在...

工具篇之IDEA功能插件HTTP_CLENT(idea2021插件)

工具描述:Java开发人员通用的开发者工具IDEA集成了HTTPClient功能,之后可以无需单独安装使用PostMan用来模拟http请求。创建方式:1)简易模式Tools->HTTPCl...

RPC、Web Service等几种远程监控通信方式对比

几种远程监控通信方式的介绍一.RPCRPC使用C/S方式,采用http协议,发送请求到服务器,等待服务器返回结果。这个请求包括一个参数集和一个文本集,通常形成“classname.meth...

《github精选系列》——SpringBoot 全家桶

1简单总结1SpringBoot全家桶简介2项目简介3子项目列表4环境5运行6后续计划7问题反馈gitee地址:https://gitee.com/yidao620/springbo...

取消回复欢迎 发表评论: