23.  类结构层次优于标签类

  有时你会碰到一个类,它的实例有一个或多个风格,并且包含一个tag属性表示实例的风格。例如,如下面的类表示一个圆或者矩形:

public class Figure {

    /**
* 标签: circle表示圆 rectangle表示矩形
*/
private String tag;
private double length;
private double width;
private double radis; public Figure(double radis) {
super();
this.radis = radis;
} public Figure(double length, double width) {
super();
this.length = length;
this.width = width;
} public double area() {
// 返回圆的面积
if ("circle".equals(tag)) {
return Math.PI * radis * radis;
} if ("rectangle".equals(tag)) {
return length * width;
} return 0;
}

  这样的类有很多缺点,代码可读性查,将来增加一个三角形又得重写计算面积的方式,而且需要增加三角形属性。

解决办法,用类层次结构代替:

package cn.qlq;

public abstract class Figure {

    abstract double area();
} class Circle extends Figure { private double radis; @Override
double area() {
return Math.PI * radis * radis;
} } class Rectangle extends Figure { private double length;
private double width; @Override
double area() {
return length * width;
} }

24.  支持使用静态成员类而不是非静态类

  有四种嵌套类:静态成员类、非静态成员类、匿名内部类和局部类。

  静态成员类是最简单的嵌套类。最好把它看做是一个普通的类,恰好在另一个类中声明,并且可以访问宿主类的成员,甚至是那些被声明为私有类的成员。其一个主要用途是作为公共帮助类,例如Calculator可以使用Calculator.Operation.PLUS。

  在语法上,静态成员和非静态成员类之间的唯一区别是静态成员类在其宿主类中用static修饰。

  如果声明了一个不需要访问宿主实例的成员类,不如用static修饰使之成为静态成员类。

25.  将源文件限制为单个顶级类

  虽然Java编译器允许在单个Java文件中定义多个顶级类,只需要文件名与public修饰的类一致即可,但是不建议这么做。

26.  不要使用原始类型

  也就是泛型尽量指明类型,虽然存在泛型擦除,但是至少编译期间会减少一些潜在的错误。比如你很容易将BigInteger和BigDecimal存入同一个集合。

比如下面就是正确的用法:(右边直接用<>即可,编译器会自动推断出正确的实际类型参数,不写<>会编译警告)

Map<String, Object> result = new HashMap<>();

泛型的术语如下:

27.  消除非检查警告

  尽可能的消除每一个未经检查的警告。

  如果不能消除警告,但你确保证明引发警告的代码是类型安全的,那么只能用@SuppressWarnings("unused")来消除警告(注意使用注解压制警告的时候写明原因)。

  总之,未经检查的警告是重要的,在运行时可能会出现ClassCaseException异常。

比如:

List<String> list = new ArrayList();

  会有一个警告:ArrayList is a raw type. References to generic type ArrayList<E> should be parameterized

写成下面这样则不会出现警告,虽然右边只是一个<>符号,并没有指定真正的实际类型参数,但编译器会根据前面的自动推断。

List<String> list = new ArrayList<>();

28.  列表优于数组

  列表与数组在两方面不同。

  首先,数组是斜变的,也就是如果Sub是Super的子类,则数组类型Sub[]是Super[]的子类型;集合是不变的,对于任何两种不同的类型Tyepe1和Type2,List<Type1>既不是List<Type2>的子类型也不是父类型。

也就是说:如下编译不通过

        // 正确编译
Object[] objects = new Long[11];

如下会报编译错误:

        // 编译不通过
List<Object> lists = new ArrayList<Long>();

  第二个差异就是数组被具体化了,也就是数组在运行时知道并强制执行它们的元素类型。如果尝试将String放入一个Long型数组是做不到的,但是通过集合不指定具体的实际类型却可以做到。

  由于上面的差异,数组和泛型不能很好的结合在一起。例如,创建泛型类型的数组,参数化类型的数组以及类型参数的数组都是非法的。因此,这些数组创建表达式都不合法:

        new List<E>();
new List<String>();
new E<String>();

补充:Java语言并不支持列表,所以一些泛型类型(如ArrayList)必须在数组上实现,其他的泛型类型,比如HashMap是为了提高性能而实现的。

29.  优先考虑泛型

  泛型类型比需要在客户端代码中强制转换的类型更安全,更易于使用。当你设计新的类型时,确保它们可以在没有这种强制转换的情况下使用。这通常意味着使类型泛型化。

  如果有任何现有的类型应该是泛型化但实际上却不是,考虑把它们泛型化。这使得这些类型的新用户的使用更容易,而不会破坏现有的客户端。

  

30.  优先使用泛型方法

  泛型类和泛型方法需要选择的时候优先使用泛型方法。

31.  使用限定通配符来增加API的灵活性(重要)

  参数化类型是不变的。比如List<String> 你就只能存放String类型的数据。

  相对于提供的不可变的类型,有时候使用限定通配符可以获得更高的灵活性。

  在API中使用通配符类型很棘手,但使得API更加灵活。一条基本的原则就是producer-extends,consumer-super(PECS)。还需要记住,所有Comparable和Comparator都是消费者。如果一个输入参数同时是生产者和消费者那么考虑使用精确类型。

如下:

package cn.qlq;

import java.util.ArrayList;
import java.util.List; public class Client { public static void main(String[] args) {
List<Integer> set1 = new ArrayList<>();
set1.add(1); List<Float> set2 = new ArrayList<>();
set2.add(1F); List<Number> unionMember = unionMember(set1, set2);
System.out.println(unionMember);
} private static <E> List<E> unionMember(List<? extends E> set1, List<? extends E> set2) {
List<E> result = new ArrayList<>();
result.addAll(set1);
result.addAll(set2); return result;
} }

  上面的unionMember方法的参数声明,两个参数set1和set2都是E的生产者所以PCES告诉我们应该用extends。

  请注意:返回类型仍然是E,不要使用限定通配符作为返回类型。

32.  合理地结合泛型和可变参数

  一般不要同时使用可变的泛型参数。

33.  优先考虑类型安全的异构容器

  

Effective.Java第23-33条(泛型相关)的更多相关文章

  1. <<Effective Java>> 第四十三条

    <<Effective Java>> 第四十三条:返回零长度的数组或者集合,而不是null 如果一个方法的返回值类型是集合或者数组 ,如果在方法内部需要返回的集合或者数组是零长 ...

  2. 《Effective Java》第5章 泛型

    第23条:请不要在新代码中使用原生态类型 声明中具有一个或者多个类型参数( type parameter)的类或者接口,就是泛型(generic)类或者接口. 每种泛型定义一组参数化的类型(param ...

  3. 《Effective Java》读书笔记 - 5.泛型

    Chapter 5 Generics Item 23: Don't use raw types in new code 虽然你可以把一个List<String>传给一个List类型(raw ...

  4. [Effective JavaScript 笔记]第33条:使构造函数与new操作符无关

    当使用函数作为一个构造函数时,程序依赖于调用者是否记得使用new操作符来调用该构造函数.注意:该函数假设接收者是一个全新的对象. 一个例子 function User(name,pwd){ this. ...

  5. Effective java读书札记第一条之 考虑用静态工厂方法取代构造器

    对于类而言,为了让client获取它自身的一个实例,最经常使用的方法就是提供一个共同拥有的构造器. 另一种放你发,也应该子每一个程序猿的工具箱中占有一席之地.类能够提供一个共同拥有的静态 工厂方法.它 ...

  6. <<Effective Java>>之善用组合而不是继承

    使用JAVA这门OO语言,第一要义就是,如果类不是专门设计来用于被继承的就尽量不要使用继承而应该使用组合 从上图2看,我们的类B复写了类A的add喝addALL方法,目的是每次调用的时候,我们就能统计 ...

  7. Effective Java 第三版——33. 优先考虑类型安全的异构容器

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  8. Java异常(二) 《Effective Java》中关于异常处理的几条建议

    概要 本章是从<Effective Java>摘录整理出来的关于异常处理的几条建议.内容包括:第1条: 只针对不正常的情况才使用异常第2条: 对于可恢复的条件使用被检查的异常,对于程序错误 ...

  9. [Java读书笔记] Effective Java(Third Edition) 第 5 章 泛型

    第 26 条:请不要使用原生态类型 声明中具有一个或多个类型参数的类或者接口,就是泛型(generic). 例如List接口只有单个类型参数E, 表示列表的元素类型.这个接口全称List<E&g ...

随机推荐

  1. Bugku 代码审计

    0x01.extract变量覆盖 代码: <?php$flag='xxx';extract($_GET);if(isset($shiyan)){$content=trim(file_get_co ...

  2. qt 子窗口内嵌到父窗口

    类声明 动态申请子窗口类对象 ClassA *a = new ClassA(this); 隐藏边框 a->setWindowFlags(Qt::CustomizeWindowHint|Qt::F ...

  3. error LNK2019: 无法解析的外部符号 _Direct3DCreate9@4,该符号在函数 "long __cdecl InitD3D(struct HWND__ *)" (?InitD3D

    出现如下错误: error LNK2019: 无法解析的外部符号 _Direct3DCreate9@4,该符号在函数 "long __cdecl InitD3D(struct HWND__ ...

  4. 【学习笔记】PYTHON语言程序设计(北理工 嵩天)

    1 Python基本语法元素 1.1 程序设计基本方法 计算机发展历史上最重要的预测法则     摩尔定律:单位面积集成电路上可容纳晶体管数量约2年翻倍 cpu/gpu.内存.硬盘.电子产品价格等都遵 ...

  5. GOJS的使用

    项目当中要求表与表之间建立关联关系,需要用到Gojs(只想说这是个什么?),以前完全没接触过gojs,所以记录下使用中的技巧和方法 http://www.devtalking.com/articles ...

  6. 2019牛客国庆day3-G &CF1238E

    牛客G: 给定大小为N的数组a[],给定M组关系,让你重排a[],使得sum{M队关系的绝对值之差}最小.首先将a排序,然后依次把a填入数组. 假设i在二进制下有x个1,用dp[i]更新dp[i|(1 ...

  7. VIJOS-P1294 拯救OIBH总部

    洛谷 P1506 拯救oibh总部 洛谷传送门 JDOJ:1405: VIJOS-P1294 拯救OIBH总部 JDOJ传送门 Description OIBH被突来的洪水淹没了> .< ...

  8. java类uuid源码分析

    通用唯一识别码(英语:Universally Unique Identifier,简称UUID)是一种软件建构的标准,亦为自由软件基金会组织在分散式计算环境领域的一部份.UUID的目的,是让分散式系统 ...

  9. Windbg的快捷键

    窗口切换 可以使用以下键盘快捷方式窗口之间进行切换. 项 效果 CTRL+TAB 调试信息窗口之间切换. 通过重复使用此密钥,你可以扫描通过的所有窗口,而不考虑是否浮动. 停靠本身,或选项卡式停靠窗口 ...

  10. css如何让父元素下的所有子元素高度相同

    小颖最近做的项目中要实现一个样式 ,小颖怕自己忘记了,写个随笔记下来 需求父元素下有多个子元素,并且子元素过多时要实现自动换行,给每个子元素都加了右边框,而每个子元素里的内容多少不一定,这就会产生右边 ...