很多使用泛型的小伙伴,都会有一个疑惑:为什么有的方法返回值前带<T>、<K, V>之类的标记,而有的方法返回值前又什么都不带呢?就像这样:

// 实体基类
class Entity {
public String toString() {
return "Entity";
}
}
// 用户类
class User extends Entity {
public String toString() {
return "User";
}
}
// 用户Dao
class UserDao {
public String toString() {
return "UserDao";
}
} /**
* 带<E>标记与不带<E>标记的比较
*/
public class GenericsClass<T extends Entity> {
// 不带标记
public void notSign(T t) {
System.out.println("notSign " + t.toString());
} // 带<T>标记
public <T> void hasSign(T e) {
System.out.println("hasSign " + e.toString());
} public static void main(String[] args) {
GenericsClass<Entity> clazz = new GenericsClass<>();
Entity entity = new Entity();
User user = new User();
UserDao userDao = new UserDao(); System.out.println("不带标记的方法:");
clazz.notSign(entity);
clazz.notSign(user);
// 不能编译通过的
// 因为在GenericsClass<T extends Entity>中已经限定了全局的T为Entity及其子类,
// 所以不能再加入UserDao;
// clazz.notSign(userDao); System.out.println("带标记的方法:");
clazz.hasSign(entity);
clazz.hasSign(user);
// 带上前缀<E>,就是在告诉编译器:这是新指定的一个类型,代表该方法自己独有的某个类,
// 跟GenericsClass<T extends Entity>中的Entity及其子类没有任何关系
// 或者说
// hasSign方法重新定义泛型T、隐藏或者代替了GenericsClass<T>中的T,不再受限于Entity及其子类
clazz.hasSign(userDao);
}
}

因此,返回值前面的<T>的作用是「重新定义泛型」,因此方法参数类型不受对象泛型类型的限制。在Java新的Stream API中有大量这种带前缀的用法,例如:

ArrayList.java:public <T> T[] toArray(T[] a)

Optional.java:public static <T> Optional<T> of(T value)

Collectors.java:public static <T> Collector<T, ?, List<T>> toList()

泛型,作为Java的一个基础特性,并不是一点毛病都没有,「泛型擦除」就是至今还未解决的一个问题(这个问题其实对于大多数人来说可以不用知道,因为实际应用中极少出现这种场景,感兴趣的话稍稍了解一下,不喜可绕过)。

所谓泛型擦除,是这样一个问题:

class User {}
class Product {}
class Shop<V> {}
class Particle<PPP, QQQ> {} public class LostInformation<T> {
public static void main(String[] args) {
// ArrayList<String>和ArrayList<Integer>应该是不同的类型,但结果是它们完全相等
Class<?> c1 = new ArrayList<String>().getClass();
Class<?> c2 = new ArrayList<Integer>().getClass();
System.out.println(c1 == c2); // 无法从泛型获得任何有关参数类型的信息
List<User> list = new ArrayList<>();
Map<User, Product> map = new HashMap<>();
Shop<User> shop = new Shop<>();
Particle<Long, Double> p = new Particle<>();
System.out.println(Arrays.toString(list.getClass().getTypeParameters()));
System.out.println(Arrays.toString(map.getClass().getTypeParameters()));
System.out.println(Arrays.toString(shop.getClass().getTypeParameters()));
System.out.println(Arrays.toString(p.getClass().getTypeParameters()));
}
}

可以看见,Particle<Long, Double>中关于Long和Double的信息已经完全丢失了,只剩下了最初的PPP和QQQ,真的应了那句话:历经千帆,归来仍是是少年!

但这是编程啊,不是文案啊~,不能这样丢掉了!

KTV和泛型(2)的更多相关文章

  1. KTV和泛型(3)

    泛型除了KTV,还有一个让人比较疑惑的玩意,而且它就是用来表达疑惑的:? 虽然通过泛型已经达到我们想要的效果了,例如: List<String> list = new ArrayList& ...

  2. java——集合、泛型、ArrayList、LinkedList、foreach循环、模拟ktv点歌系统

    集合:一系列特殊的类,这些类可以存储任意类型的对象,长度可变,集合类都在java.util包中. 但是集合记不住对象的类型,当把对象从集合中取出时这个对象的编译类型就变成了Object类型.这样在取元 ...

  3. 一起学 Java(三) 集合框架、数据结构、泛型

    一.Java 集合框架 集合框架是一个用来代表和操纵集合的统一架构.所有的集合框架都包含如下内容: 接口:是代表集合的抽象数据类型.接口允许集合独立操纵其代表的细节.在面向对象的语言,接口通常形成一个 ...

  4. .NET面试题系列[8] - 泛型

    “可变性是以一种类型安全的方式,将一个对象作为另一个对象来使用.“ - Jon Skeet .NET面试题系列目录 .NET面试题系列[1] - .NET框架基础知识(1) .NET面试题系列[2] ...

  5. C#4.0泛型的协变,逆变深入剖析

    C#4.0中有一个新特性:协变与逆变.可能很多人在开发过程中不常用到,但是深入的了解他们,肯定是有好处的. 协变和逆变体现在泛型的接口和委托上面,也就是对泛型参数的声明,可以声明为协变,或者逆变.什么 ...

  6. 编写高质量代码:改善Java程序的151个建议(第7章:泛型和反射___建议106~109)

    建议106:动态代理可以使代理模式更加灵活 Java的反射框架提供了动态代理(Dynamic Proxy)机制,允许在运行期对目标类生成代理,避免重复开发.我们知道一个静态代理是通过主题角色(Prox ...

  7. 6.在MVC中使用泛型仓储模式和依赖注入实现增删查改

    原文链接:http://www.c-sharpcorner.com/UploadFile/3d39b4/crud-operations-using-the-generic-repository-pat ...

  8. C#泛型详解(转)

    初步理解泛型: http://www.cnblogs.com/wilber2013/p/4291435.html 泛型中的类型约束和类型推断 http://www.cnblogs.com/wilber ...

  9. C# 泛型

    C# 泛型 1.定义泛型类 在类定义中包含尖括号语法,即可创建泛型类: class MyGenericClass<T> { //Add code } 其中T可以遵循C#命名规则的任意字符. ...

随机推荐

  1. Nginx listen、server_name、location的配置

    # Nginx静态资源的配置指令 # listen指令 # 语法 listen address[:port][default_server] # 如: listen 127.0.0.1:8000: # ...

  2. Docker Compose之容器编排开发初探

    1.前言 Docker Compose 是 Docker 官方编排(Orchestration)项目之一,负责快速在集群中部署分布式应用. Compose 是一个用于定义和运行多个 Docker 应用 ...

  3. Linux下一键安装Python3&更改镜像源&虚拟环境管理技巧

    前言 之前分享过一篇<Linux系统自带Python2&yum的卸载及重装>,介绍了如何卸载及重装Linux(CentOS)自带的的Python2.7.今天主要介绍如何在Linux ...

  4. java-修饰词、抽象类、抽象方法

    1.final:最终的.不可改变的------单独应用的机率小 1)修饰变量:变量不能被改变 2)修饰方法:方法不能被重写 3)修饰类:类不能被继承 2.static final:常量,应用率高 1) ...

  5. 微服务性能分析|Pyroscope 集合 Spring Cloud Pig 的实践分享

    随着微服务体系在生产环境落地,也会伴随着一些问题出现,比如流量过大造成某个微服务应用程序的性能瓶颈.CPU利用率高.或内存泄漏等问题.要找到问题的根本原因,我们通常都会通过日志.进程再结合代码去判断根 ...

  6. 在 WXML 中直接使用 JS 代码

    因为有在 Vue 下开发应用的习惯,希望能够直接在 wxml 中的标签里使用 JS 代码.微信小程序其实也是可以的,在使用 JS 代码的时候需要用{{}}来包裹起来. 以下是在 wxml 中使用 JS ...

  7. 「2020-2021 集训队作业」Yet Another Linear Algebra Problem(行列式,Binet-Cauchy 公式)

    题面 出题人:T L Y \tt TLY TLY 太阳神:Tiw_Air_OAO 「 2020 - 2021 集 训 队 作 业 」 Y e t A n o t h e r L i n e a r A ...

  8. 图床搭建|chrome插件|操作简单不要钱

    为什么需要一个图床 为了写博客 图床:专门用来存放图片,同时允许你把图片对外连接的网上空间. 用markdown格式写作,插入图片需要图片的url地址,然后博客的背景图也是要用url地址. 有段时间, ...

  9. Controller以及RestFul风格

    Controller以及RestFul风格 控制器Controller 控制器复杂提供访问应用程序的行为,通常通过接口定义或注解定义两种方式实现 控制器负责解析用户的请求并将其转换为一个模型 在Spr ...

  10. 如何不编写 YAML 管理 Kubernetes 应用?

    Kubernetes 将自身边界内的事物都抽象为资源.其中的主要部分,是以 Deployment.StatefulSet 为代表的 workload 工作负载控制器,其他各类资源都围绕这些主要的资源工 ...