Java 9:装B之前你必须要会的——泛型,注解,反射

2023-11-18

1 泛型

1.1 基本概念

泛型提供了编译期的类型检查,但问题远非这么简单

///原生态类型
List list1 = new ArrayList();   ///规避的类型检查
List list1 = new ArrayList<String>();

///参数化类型
List<Object> list2 = new ArrayList<Object>();  //可以放任何类型的对象,没指导意义
List<String> list3 = new ArrayList<String>();

///通配符类型
//无限制通配符类型
List<?> list4 = new ArrayList<Dog>();
//有限制通配符类型
List<? extends Animal> list4 = new ArrayList<Dog>();
List<? extends Animal> list4 = new ArrayList<Cat>();


List<String>里的String是:实际类型参数

在类和方法定义时,引入形式类型参数
public class List<T>{
    T[] data;
}
这里T是形式类型参数
List<T>就叫做:泛型

public class PrimitiveList<E extends Number>{
    E[] data;
}
<E extends Number>是有限制通配符类型


泛型方法
public <E> void do_sth(E e){

}

类型令牌
String.class
List.class
String[].class

带泛型的类型可以降级为不带泛型的类型


public static void main(){
    List<String> strings = new ArrayList<String>();
    unsafeAdd(strings, new Integer(42));
    String s = strings.get(0);
}

private static void unsafeAdd(List list, Object o){
    list.add(o);  //unchecked call to add(E) in raw type list
}
这段代码可以通过编译,
但会收到警告,unchecked call to add(E) in raw type list,
而且明显在运行时会收到ClassCastException

这种使用方式并不是一无是处,如果unsafeAdd是一个不关心List具体泛型类型的实现逻辑,
或者没法知道List的泛型类型,或者需要处理各种泛型类型的List,则这种写法是可取的

如果这样,则无法通过编译
private static void unsafeAdd(List<Object> list, Object o){
    list.add(o);  //unchecked call to add(E) in raw type list
}

无限制的通配符类型

如果一个方法需要传入List,但不关心List的具体泛型类型,则可以使用一个问号代替泛型,
这就是无限制的通配符

static int numElementsInCommon(Set<?> s1, Set<?> s2){
    int result = 0;
    for(Object o1: s1){
        if(s2.contains(o1))){
            result++;
        }

    }
    return result;
}

Set<?> set1 = ..;
Set set2 = ..;
set1和set2的区别是什么?
1 这两个都能接受所有泛型类型的Set
2 但是set1只能add一个类型的对象,set2可以add任何类型的对象

有限制的通配符类型

Set<? extends Aminal> set = ..;


对于Stack<E>,如果提供如下方法
public void pushAll(Iterable<? exnteds E> src){
    for(E e: src){
        push(e);
    }
}
试想,如果参数不用<? exnteds E>,怎么样才合适呢
<? exnteds E>在这里就是最好的选择


对于Stack<E>,如果提供如下方法
public void popAll(Collection<E> dst){
    while(!isEmpty()) dst.add(pop());
}
如果是Stack<Dog>,本来你用List<Dog>作为参数传入,可以
但如果你要用List<Animal>传入,就不行了,但逻辑上这么做是没问题的,所以方法一改:
public void popAll(Collection<? super E> dst){

}
意思是Collection的泛型类型是E的某个超类


所以对于泛型类型对应的对象E e
如果你要生产(put),即给e值,则E和E的子类都可以给,<? extends E>
如果你要消费(get),即x = e,则x即可以是E,也可以是E的基类或接口,<? super E>
这就是PECS原则,Producer extends,Consumer super

1.2 关于class对象:

List.class
String[].class
int.class
都是合法的

但是List.class不合法, List


if(o instanceof Set){
    Set<?> m = (Set<?>)o;
}

但你不能 o instanceof Set<String>

1.3 消除非受检警告:

情况1:


Set<Animal> animals = new HashSet();
这种情况可以很容易的得到编译器提醒unchecked

情况2:
如果你确定代码是类型安全的,可以@SuppressWarnings(“unchecked”)

1.4 数组和集合:


数组是协变的
Animal[] animals;
Dog[] dogs = new Dog[2];
animals = dogs;  //对的
animals[0] = new Cat(); //错的,ArrayStoreException

泛型是不可变的
List<Animal> animals;
List<Dog> dogs = new ArrayList<Dog>();
animals = dogs;  //错误的,编译报类型不匹配

数组是具体化的,reified,所以数组会在运行时才知道并检查他们的元素类型约束
泛型是通过擦除的,erasure,只能在编译时强化其类型信息,运行时丢弃

有泛型信息时,不能创建数组,以下都非法
List<E>[]
new List<String>[]
new E[]
为什么不可以这么创建数组呢,看下面的例子,如果可以这么创建数组的话:
List<String>[] stringLists = new List<String>[1];
List<Integer> intList = Arrays.asList(42);
Object[] objects = stringLists;
Object[0] = intList;
String s = stringLists[0].get(0);
上述代码的错误如果放到运行时,就是ClassCast错误,所以编译时第一行就会报错

E, List<E>, List<String>
这些都是不可具体化的类型,其运行时表示法包含的信息,要少于其编译时表示法包含的信息

注意:创建List<?>[] arr是合法的

1.5 泛型的类型推导

很复杂,规则很多,据说光文档就有16页

Lang.isEmpty(s)这种形式叫做显式类型参数

1.6 运行时获取泛型的具体类型:

参考TypeToken

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

public class TypeToken<T> {

    private final Type type;

    protected TypeToken(){
        Type superClass = getClass().getGenericSuperclass();

        type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
    }

    public Type getType() {
        return type;
    }

    public final static Type LIST_STRING = new TypeToken<List<String>>() {}.getType();



}


System.out.println(new TypeToken<List<Animal>>(){}.getType());
打印:
java.util.List<com.cowthan.Animal>

2 注解

注解是元数据

2.1 内置注解

提醒编译器的
@Override
@Deprecated
@SuppressWarnings 关闭警告

2.2 自定义注解

四大元注解

  • @Target:注解能用在哪儿
    • @Target(ElementType.METHOD)
    • ElementType可能的值:
      • TYPE 用于class定义
      • CONSTRUCTOR 用于构造方法
      • METHOD 用于方法
      • FIELD 用于成员变量
      • LOCAL_VARIABLE 局部变量声明
      • PACKAGE 包声明
      • PARAMETER 方法参数声明
      • ANNOTATION_TYPE
      • TYPE_PARAMETER
      • TYPE_USE
  • @Retention:注解信息在哪个阶段有效
    • @Retention(RetentionPolicy.RUNTIME)
    • RetentionPolicy可能的值:
      • SOURCE:源码阶段,编译时,一般是用来告诉编译器一些信息,将被编译器丢弃
      • CLASS:注解在class文件中可用,会被VM丢弃
      • RUNTIME:运行时,VM在运行时也保留注解,这个才能通过反射获取到
  • Documented
    • 将此注解包含在JavaDoc中
  • Inherited
    • 允许子类继承父类中的注解

注解 处理器:通过反射拿到注解

2.3 自定义注解实例

简单的例子,只展示了如何定义和使用注解,没有阐明如何处理注解

/**
 * 描述一个测试用例
 *
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {

    public int id();
    public String description() default "no description";

}
public class UseCaseDemo {

    @UseCase(id = 1, description = "密码的格式必须规范")
    public boolean testPassword(String password){
        return false;
    }

    @UseCase(id = 2, description = "用户名的格式必须规范")
    public boolean testUsername(String username){
        return false;
    }

    @UseCase(id = 3, description = "新密码不能等于旧密码")
    public boolean testNewPwd(String password){
        return false;
    }
}

2.4 处理注解:运行时

下面是一个简单的序列化框架,使用了注解@Seriable的字段可以被序列化为json

@Target({ ElementType.FIELD, ElementType.TYPE })  
@Retention(RetentionPolicy.RUNTIME)  
public @interface Seriable  
{  

}  


public class User  
{  
    @Seriable  
    private String username;  
    @Seriable  
    private String password;  

    private String three;  
    private String four;  
}  

public boolean isSeriable(Field f){
    f.setAccessible(true);
    Seriable annotation = f.getAnnotation(Seriable.class);
    if(id == null){
        return false;
    }else{
        return true;
    }
}

2.5 处理注解:编译期

ButterKnife能根据注解生成代码,怎么做到的呢,涉及到apt和编译期处理器

http://www.cnblogs.com/avenwu/p/4173899.html

http://www.bubuko.com/infodetail-826234.html 这篇讲的挺好

http://www.jianshu.com/p/1910762593be 安卓版

http://blog.csdn.net/lmj623565791/article/details/43452969

  • 重点有三个
    • 定义注解和注解处理器
    • 打成jar包
    • 在任何项目里使用注解,编译时触发注解处理器

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.Elements;

@SupportedAnnotationTypes({"com.avenwu.annotation.PrintMe"})
public class BeanProcessor extends AbstractProcessor {

    // 元素操作的辅助类
    Elements elementUtils;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        // 元素操作的辅助类
        elementUtils = processingEnv.getElementUtils();
    }


    @Override
    public boolean process(Set<? extends TypeElement> annotations,
            RoundEnvironment roundEnv) {

        for (TypeElement currentAnnotation : annotations) {
            Name qualifiedName = currentAnnotation.getQualifiedName();
            if (qualifiedName.contentEquals("com.avenwu.annotation.PrintMe")){
                Set<? extends Element> annotatedElements = roundEnv.getElementsAnnotatedWith(currentAnnotation);
                for (Element element : annotatedElements) {
                    Version v = element.getAnnotation(Version.class);
                    int major = v.major();
                    int minor = v.minor();
                    if(major < 0 || minor < 0) {
                        String errMsg = "Version cannot be negative. major = " + major + " minor = " + minor;
                        Messager messager = this.processingEnv.getMessager();
                        messager.printMessage(javax.tools.Diagnostic.Kind.ERROR,errMsg,element);
}
                }
            }
        }
        return true;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

}

3 反射

反射等以后吧,安卓里面反射用的也不多,EventBus2就是基于反射,在EventBus3里已经
改成了预处理注解的方式

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java 9:装B之前你必须要会的——泛型,注解,反射 的相关文章

  • 如何用Java写入OS系统日志?

    Mac OS 有一个名为 Console 的应用程序 其中包含记录的消息 错误和故障 我相信 Windows 中的等效项是事件查看器 我想 Linux 上也有一个 但我不知道它是什么 也不知道它在哪里 是否可以像这样从 Java 输出获取消
  • Java Sqlite Gradle

    我对 gradle 和 java 还很陌生 我有一个使用 sqlite 的项目 它通过 intellij idea 运行良好 但我无法从终端运行它 它会抛出异常 java lang ClassNotFoundException org sq
  • Java中定义类型后同时初始化多个变量?

    这里需要一些语法方面的帮助 我正在尝试在定义类型后重新初始化多个变量 例如 int bonus sales x y 50 这工作正常 但是我想稍后在程序中将不同的值放入其中一些变量中 但我收到语法错误 bonus 25 x 38 sales
  • 以点作为分隔符分割字符串

    我想知道我是否要在一个字符串上分割字符串 正确的方式 我的代码是 String fn filename split return fn 0 我只需要字符串的第一部分 这就是我返回第一项的原因 我问这个是因为我在 API 中注意到 意味着任何
  • WebLogic 10 中的临时目录

    每当 WL 停止时 它都不会删除其临时目录 即 domains mydomain servers myserver tmp WL TEMP APP DOWNLOADS domains mydomain servers myserver tm
  • 使用 JAXB 编组 LocalDate

    我正在构建一系列链接类 我希望能够将其实例编组到 XML 以便我可以将它们保存到文件中并稍后再次读取它们 目前我使用以下代码作为测试用例 import javax xml bind annotation import javax xml b
  • Maven 目标的默认阶段?

    据我了解 在 Maven 中 插件目标可以附加到生命周期阶段 如果没有定义 默认阶段是什么 根据我的经验 这取决于插件的目标 例如 组装 单个 http maven apache org plugins maven assembly plu
  • BigDecimal 的 JPA @Size 注释

    我该如何使用 SizeMySQL 的注释DECIMAL x y 列 我在用着BigDecimal 但是当我尝试包括 Size max它不起作用 这是我的代码 Size max 7 2 Column name weight private B
  • 通过 JNI 从 Applet 调用 DLL

    我有一个 概念验证 的作品 它跨越了一些不熟悉的领域 我的任务是将 EFTPOS 机器连接到在内联网浏览器中作为小程序运行的应用程序 我暂时忽略了 EFTPOS dll 并用我选择的语言 Delphi 创建了一个简单的 JNI 修饰的 DL
  • 更改 RowLayout SWT Java 中元素的顺序

    有没有办法更改在行布局中创建的元素的顺序 我想将其显示在元素中 首先显示 例如 如果我创建 element1 则 element2 element3 element4 我想看到的布局为 元素4 元素3 元素2 元素1 这意味着最后创建的元素
  • 删除 ArrayList 对象问题

    我在处理作业时遇到从 ArrayList 中删除对象的问题 如果我使用 正常 for 循环 它的工作原理如下 public void returnBook String isbn for int i 0 i lt booksBorrowed
  • 膨胀类片段 InflateException 二进制 XML 文件时出错

    我正在使用 Material Design 和 NavigationDrawer 布局等设计我的第一个应用程序 但我遇到了一个问题 该应用程序非常简单 它只显示文本 并且基于 Android Studio 中提供的模板 尝试启动我的应用程序
  • Hibernate @OneToMany 注释到底是如何工作的?

    我对 Hibernate 还很陌生 我正在通过教程学习它 我在理解到底如何一对多注释作品 所以我有这两个实体类 Student代表一个学生并且Guide代表指导学生的人 因此 每个学生都与一名向导相关联 但一名向导可以跟随多个学生 我想要一
  • C++ 中的 Java ArrayList [重复]

    这个问题在这里已经有答案了 在Java中我可以做 List
  • Java 8根据Map属性过滤Map对象列表以删除一些重复项

    Have a List
  • 如何将任务添加到 gradle 中的主要“构建”任务

    当我尝试使用以下代码将任务添加到主构建任务时 rootProject tasks getByName build dependsOn mytask 当我跑步时它抱怨gradle w build输出 Where Build file line
  • 设计抽象类时是否应该考虑序列化问题?

    一般来说这个问题来自Eclipse建议在抽象类上添加串行版本UID 由于该类是抽象类 因此该类的实例永远不会存在 因此它们永远不会被序列化 只有派生类才会被序列化 所以我的问题是放置一个安全 SuppressWarnings serial
  • 如何使用 SAX Java 解析器读取注释文本

    我只想使用 Java 中的 SAX 解析器读取 XML 文件中对象标记的注释 这是我的文件的摘要
  • SWT - 与操作系统无关的获取等宽字体的方法

    SWT 有没有一种方法可以简单地获得跨各种操作系统的等宽字体 例如 这适用于 Linux 但不适用于 Windows Font mono new Font parent getDisplay Mono 10 SWT NONE 或者我是否需要
  • 在多线程环境中,Collections.sort 方法有时会抛出 ConcurrentModificationException。列表没有进行结构性修改

    package CollectionsTS import java util ArrayList import java util Collections import java util HashSet import java util

随机推荐

  • 什么是机器学习

    转载 博文计算机的潜意识之从机器学习谈起 原文请点击链接 https www cnblogs com subconscious p 4107357 html 强烈推荐一篇关于机器学习入门导论的博文 里面的内容非常详实 加上作者的理解和感悟精
  • Windows7上安装pytorch1.11后报api-ms-win-core-path-l1-1-0.dll错误的解决方法

    在Windows7上通过Anaconda安装PyTorch v1 11 0后 执行程序时报如下图所示错误 无法启动此程序 因为计算机中丢失api ms win core path l1 1 0 dll 尝试重新安装该程序以解决此问题 此Wi
  • Java爬虫与Python爬虫有什么区别

    Java爬虫和Python爬虫是两种常见的网络爬虫实现方式 它们在语言特性 开发环境和生态系统等方面存在一些区别 1 语言特性 Java是一种面向对象的编程语言 而Python是一种脚本语言 Java较为严谨 需要明确定义类 方法和变量 而
  • MDT 2013 从入门到精通之软件自动化部署设置

    因为工作时间原因已经很长一段时间没有更新博客 还请大伙见谅哈 有关MDT系列文章也是很久没有更新了 今天就来谈谈一些常规技巧内容 我们在日常使用MDT部署过程中 很多新手总是纠结于软件的安装问题 总是通过SkipApplications N
  • h5页面loading丝滑小妙招,vue+vant

    1 v if 使用v if tag 1 在data声明一个变量tag 0 请求到参数后tag 1 我会在created重新初始化tag 0 为了保险我还会加一个setTimeout定时器 div class main div data re
  • java项目的远程调试

    我们在工作中可能会遇到这样的场景 有时候有个问题在本地环境不重现开发或者测试环境的问题 而这个问题需要急需解决的情况 更有部分项目在本地无法启动 需要依赖在服务器启动 有时候可以尝试远程调试 我这里用springboot项目 做一下演示 在
  • phpexcel导出

    fileName 亚马逊品类数据 date Y m d fileType xlsx sql select a sku b product typename c category status a gender a sales status
  • 数据库表与表的三种方式

    表和表之间 一般就是三种关系 一对一 一对多 多对多 1 一对一 数据库表中的数据结构 我们用人与车一 一对应的方式来描述一对一的数据表结构 type是区分这条数据是人还是车 master对应是的主人 车的主人是哪个id car对应的是那辆
  • 示波器探头碰人的波形,人碰示波器探头的波形

    如上图所示 如图中 点说明电流恒定 导体切割磁场线 向导线方向切割磁场变强 远离导线切割磁场变弱 则图中 点说明导体不动 但是导线电流增大则磁场强度增加 等效成导体往恒定电流磁场切割 导线电流减小则磁场减小 等效成导体往恒定电流磁场反方向切
  • 「 标准 」NTSC、PAL、SECAM 三大制式简介

    NTSC National Televison System Committee 制式 NTSC 电视标准 每秒 29 97 帧 简化为 30 帧 电视扫描线为 525 线 偶场在前 奇场在后 标准的数字化 NTSC 电视标准分辨率为720
  • KubeVela 正式开源:一个高可扩展的云原生应用平台与核心引擎

    来源 阿里巴巴云原生公众号 美国西部时间 2020 年 11 月 18 日 在云原生技术 最高盛宴 的 KubeCon 北美峰会 2020 上 CNCF 应用交付领域小组 CNCF SIG App Delivery 与 Open Appli
  • 信号处理——梅尔滤波器(MFCC)

    信号处理 梅尔滤波器 MFCC 一 概述 在语音识别 Speech Recognition 和话者识别 Speaker Recognition 方面 最常用到的语音特征就是梅尔倒谱系数 Mel scale FrequencyCepstral
  • NoSQL系统的分类

    什么是NoSQL系统 采用最终一致性的数据库系统 统称为NoSQL Not only SQL 系统 根据数据模型的不同 NoSQL系统又分为以下几类 基于键值对的 Memcached Redis 基于列存储的 Bigtable Apache
  • 小米路由器3/3G/4通过串口(ttl)刷机

    准备工作 淘宝购买 USB转TTL CH340模块 杜邦线 排针 https detail tmall com item htm id 525204252260 spm a1z09 2 0 0 19dc2e8doubZVx u blagqs
  • 查看linux下安装了哪些软件

    1 查看是否安装了gcc 命令 rpm ql gcc rpm qa grep gcc 参数 q 询问 a 查询全部 l 显示列表 2 权限 安装和删除只有root和有安装权限的用户才可以进行 查询是每个用户都可以进行操作的 RPM 的介绍和
  • 《Docker 镜像操作》

    Docker 镜像原理 1 Docker 镜像本质是什么 是一个分层文件系统 2 Docker 中一个 centos 镜像为什么只有 200MB 而一个 centos 操作系统的 iso 文件要几个个 G Centos 的 iso 镜像文件
  • IDEA插件-PlantUML

    一 idea安装plantUml插件 在idea中Preferences gt plugins gt Browse repositories gt 搜索 plantUML gt 安装即可 二 通过 brew 安装 Graphviz 安装pl
  • [极客大挑战 2019]RCE ME(取反、异或绕过正则表达式、bypass disable_function)

    题目进去后 很简单的代码 显然命令执行 看到了eval 应该是用system等函数来实现命令执行 但是得要先绕过preg match 中正则表达式的限制 一开始傻乎乎的直接传了个数组 妄图绕过preg match 这很显然是不行的 附上大佬
  • c语言 push,深入了解C语言(局部变量的定义)

    深入了解C语言 这一节我们主要来研究一下C语言如何使用函数中的局部变量的 C语言中对于全局变量和局部变量所分配的空间地址是不一样的 全局变量是放在 DATA段 也就是除开 TEXT代码段的另一块集中的内存空间 而局部变量主要是使用堆栈的内存
  • Java 9:装B之前你必须要会的——泛型,注解,反射

    1 泛型 1 1 基本概念 泛型提供了编译期的类型检查 但问题远非这么简单 原生态类型 List list1 new ArrayList 规避的类型检查 List list1 new ArrayList