mirror of
https://github.com/Jueee/effective-Java.git
synced 2024-12-25 20:30:08 +08:00
枚举和注解
This commit is contained in:
parent
85d02fd51a
commit
2edf799df1
BIN
Effective-Java.jpg
Normal file
BIN
Effective-Java.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 41 KiB |
@ -1,5 +1,7 @@
|
||||
# 说明
|
||||
|
||||
![](Effective-Java.jpg)
|
||||
|
||||
### 目录
|
||||
|
||||
#### ch02.[创建和销毁对象](ch02创建和销毁对象)
|
||||
|
@ -26,4 +26,5 @@
|
||||
|
||||
该条目中描述的模式在 `Java` 类库中有所使用。例如,`java.nio.file.LinkOption` 枚举类型实现了 `CopyOption` 和 `OpenOption` 接口。
|
||||
|
||||
**总之,虽然不能编写可扩展的枚举类型,但是你可以编写一个接口来配合实现接口的基本的枚举类型,来对它进行模拟。** 这允许客户端编写自己的枚举(或其它类型)来实现接口。如果 `API` 是根据接口编写的,那么在任何使用基本枚举类型实例的地方,都可以使用这些枚举类型实例。
|
||||
**总之,虽然不能编写可扩展的枚举类型,但是你可以编写一个接口来配合实现接口的基本的枚举类型,来对它进行模拟。** 这允许客户端编写自己的枚举(或其它类型)来实现接口。如果 `API` 是根据接口编写的,那么在任何使用基本枚举类型实例的地方,都可以使用这些枚举类型实例。
|
||||
|
||||
|
101
ch06枚举和注解/39.注解优于命名模式.md
Normal file
101
ch06枚举和注解/39.注解优于命名模式.md
Normal file
@ -0,0 +1,101 @@
|
||||
## 注解优于命名模式
|
||||
|
||||
过去,通常使用 **命名模式**(naming patterns)来指示某些程序元素需要通过工具或框架进行特殊处理。
|
||||
|
||||
但命名模式有几个很大的缺点:
|
||||
|
||||
1. 拼写错误导致失败,但不会提示。
|
||||
2. 无法确保它们仅用于适当的程序元素。
|
||||
3. 没有提供将参数值与程序元素相关联的好的方法。
|
||||
|
||||
**注解** 很好地解决了所有这些问题。
|
||||
|
||||
我们将编写我们自己的测试框架来显示注解的工作方式。
|
||||
|
||||
假设你想定义一个注解类型来指定自动运行的简单测试,并且如果它们抛出一个异常就会失败。
|
||||
|
||||
- [Item39Example01.java](EnumsAnnotations/src/main/java/com/jueee/item39/Item39Example01.java):**声明注解类型**
|
||||
|
||||
注解类型的声明本身使用 `Retention` 和 `Target` 注解进行标记。
|
||||
|
||||
注解类型声明上的这种注解称为元注解。
|
||||
|
||||
`@Retention`(`RetentionPolicy.RUNTIME`)元注解指示 `Test` 注解应该在运行时保留。
|
||||
|
||||
没有它,测试工具就不会看到 `Test` 注解。
|
||||
|
||||
`@Target.get(ElementType.METHOD)`元注解表明 `Test` 注解只对方法声明合法:它不能应用于类声明,属性声明或其他程序元素。
|
||||
|
||||
- [Item39Example02.java](EnumsAnnotations/src/main/java/com/jueee/item39/Item39Example02.java):[Item39Example01.java](EnumsAnnotations/src/main/java/com/jueee/item39/Item39Example01.java) 注解在实践中的应用。
|
||||
|
||||
未使用 `Item39Example01` 注解标注的四种方法将被测试工具忽略。
|
||||
|
||||
`Item39Example01` 注解对 `Item39Example02` 类的语义没有直接影响。
|
||||
|
||||
他们只提供信息供相关程序使用。
|
||||
|
||||
更一般地说,注解不会改变注解代码的语义。
|
||||
|
||||
- [Item39Example03.java](EnumsAnnotations/src/main/java/com/jueee/item39/Item39Example03.java):可以通过诸如这个简单的测试运行器等工具对其进行特殊处理。
|
||||
|
||||
测试运行器工具接受完全限定的类名,并通过调用 `Method.invoke` 来反射地运行所有类标记有 `Item39Example01` 注解的方法。
|
||||
|
||||
`isAnnotationPresent` 方法告诉工具要运行哪些方法。
|
||||
|
||||
如果测试方法引发异常,则反射机制将其封装在 `InvocationTargetException` 中。
|
||||
|
||||
该工具捕获此异常并打印包含由 `test` 方法抛出的原始异常的故障报告,该方法是使用 `getCause` 方法从 `InvocationTargetException` 中提取的。
|
||||
|
||||
打印结果:
|
||||
|
||||
```
|
||||
public static void com.jueee.item39.Item39Example02.m3() failed: java.lang.RuntimeException: Boom
|
||||
public static void com.jueee.item39.Item39Example02.m7() failed: java.lang.RuntimeException: Crash
|
||||
Invalid @Item39Example01: public void com.jueee.item39.Item39Example02.m5()
|
||||
Passed: 1, Failed: 3
|
||||
```
|
||||
#### 添加对仅在抛出特定异常时才成功的测试的支持
|
||||
|
||||
- [Item39Example04.java](EnumsAnnotations/src/main/java/com/jueee/item39/Item39Example04.java):添加一个新的注解类型。
|
||||
|
||||
此注解的参数类型是 `Class<? extends Throwable>`。
|
||||
|
||||
它表示“扩展 `Throwable` 的某个类的 `Class` 对象”,它允许注解的用户指定任何异常(或错误)类型。
|
||||
|
||||
- [Item39Example05.java](EnumsAnnotations/src/main/java/com/jueee/item39/Item39Example05.java):注解在实际中的例子。
|
||||
|
||||
- [Item39Example06.java](EnumsAnnotations/src/main/java/com/jueee/item39/Item39Example06.java):修改测试运行器工具来处理异常测试的新版本。
|
||||
|
||||
#### 执行多值注解
|
||||
|
||||
从 Java 8 开始,还有另一种方法来执行多值注解。
|
||||
|
||||
- [Item39Example07.java](EnumsAnnotations/src/main/java/com/jueee/item39/Item39Example07.java):可以使用 `@Repeatable` 元注解来标示注解的声明,而不用使用数组参数声明注解类型,以指示注解可以重复应用于单个元素。
|
||||
|
||||
- [Item39Example08.java](EnumsAnnotations/src/main/java/com/jueee/item39/Item39Example08.java):该元注解采用单个参数,该参数是包含注解类型的类对象,其唯一参数是注解类型的数组。
|
||||
|
||||
- [Item39Example09.java](EnumsAnnotations/src/main/java/com/jueee/item39/Item39Example09.java):测试使用重复注解代替数组值注解。
|
||||
|
||||
- [Item39Example10.java](EnumsAnnotations/src/main/java/com/jueee/item39/Item39Example10.java):处理可重复注解。重复注解生成包含注解类型的合成注解。
|
||||
|
||||
`getAnnotationsByType` 方法掩盖了这一细节,可以用于访问可重复注解类型的重复注解和非重复注解。
|
||||
|
||||
但是 `isAnnotationPresent` 明确指出,重复注解不是注解类型,而是包含注解的类型。
|
||||
|
||||
如果一个元素具有某种类型的重复注解,并且您使用 `isAnnotationPresent` 方法检查该元素是否具有该类型的注解,您将发现它没有。
|
||||
|
||||
因此,使用此方法检查注解类型是否存在,将导致程序无声地忽略重复的注解。
|
||||
|
||||
类似地,使用此方法检查包含的注解类型将导致程序无声地忽略非重复注解。
|
||||
|
||||
要使用 `isAnnotationPresent` 检测重复和非重复注解,您需要检查注解类型及其包含的注解类型。
|
||||
|
||||
添加可重复的注解是为了提高源代码的可读性,源代码逻辑上将相同注解类型的多个实例应用于给定的程序元素。
|
||||
|
||||
如果您觉得它们增强了源代码的可读性,那么就使用它们,但是请记住,在声明和处理可重复注解时有更多的样板文件,而且处理可重复注解很容易出错。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
17
ch06枚举和注解/40.始终使用Override注解.md
Normal file
17
ch06枚举和注解/40.始终使用Override注解.md
Normal file
@ -0,0 +1,17 @@
|
||||
## 始终使用 Override 注解
|
||||
|
||||
Java 类库包含几个注解类型。对于典型的程序员来说,最重要的是 `@Override` 。
|
||||
|
||||
此注解只能在方法声明上使用,它表明带此注解的方法声明重写了父类的声明。
|
||||
|
||||
如果始终使用这个注解,它将避免产生大量的恶意 bug。
|
||||
|
||||
#### 示例:表示一个 双字母组,或有序的字母对
|
||||
|
||||
主程序反复向一个集合添加26个bigram,每个bigram由两个相同的小写字母组成。然后它打印集合的大小。
|
||||
|
||||
- [Item40Example01.java](EnumsAnnotations/src/main/java/com/jueee/item40/Item40Example01.java):没有重写equals,而是重载了它(item 52)。
|
||||
|
||||
为了覆盖 Object 的 equals 方法,我们必须定义一个参数是Object类型的equals方法,但是 Bigram 的 equals 方法的参数不是 Object 类型的,所以 Bigram 继承了 Object 的 equals 方法。这个 equals 方法测试对象标识,就像 == 操作符一样。每一个双字母的十份副本都与其他九份不同,因此它们被认为是不相等的。
|
||||
|
||||
- [Item40Example02.java](EnumsAnnotations/src/main/java/com/jueee/item40/Item40Example02.java):使用 Override 注解,重写equals。
|
@ -3,11 +3,13 @@ package com.jueee.item38;
|
||||
//Emulated extension enum
|
||||
public enum ExtendedOperation implements Operation {
|
||||
EXP("^") {
|
||||
@Override
|
||||
public double apply(double x, double y) {
|
||||
return Math.pow(x, y);
|
||||
}
|
||||
},
|
||||
REMAINDER("%") {
|
||||
@Override
|
||||
public double apply(double x, double y) {
|
||||
return x % y;
|
||||
}
|
||||
|
@ -0,0 +1,12 @@
|
||||
package com.jueee.item39;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Item39Example01 {
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.jueee.item39;
|
||||
|
||||
public class Item39Example02 {
|
||||
|
||||
@Item39Example01
|
||||
public static void m1() { } // Item39Example01 should pass
|
||||
|
||||
public static void m2() { }
|
||||
|
||||
@Item39Example01
|
||||
public static void m3() { // Item39Example01 should fail
|
||||
throw new RuntimeException("Boom");
|
||||
}
|
||||
|
||||
public static void m4() { }
|
||||
|
||||
@Item39Example01
|
||||
public void m5() { } // INVALID USE: nonstatic method
|
||||
|
||||
public static void m6() { }
|
||||
|
||||
@Item39Example01
|
||||
public static void m7() { // Item39Example01 should fail
|
||||
throw new RuntimeException("Crash");
|
||||
}
|
||||
|
||||
public static void m8() { }
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package com.jueee.item39;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class Item39Example03 {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String testClassName = "com.jueee.item39.Item39Example02";
|
||||
|
||||
int tests = 0;
|
||||
int passed = 0;
|
||||
Class<?> testClass = Class.forName(testClassName);
|
||||
for (Method m : testClass.getDeclaredMethods()) {
|
||||
if (m.isAnnotationPresent(Item39Example01.class)) {
|
||||
tests++;
|
||||
try {
|
||||
m.invoke(null);
|
||||
passed++;
|
||||
} catch (InvocationTargetException wrappedExc) {
|
||||
Throwable exc = wrappedExc.getCause();
|
||||
System.out.println(m + " failed: " + exc);
|
||||
} catch (Exception exc) {
|
||||
System.out.println("Invalid @Item39Example01: " + m);
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.printf("Passed: %d, Failed: %d%n", passed, tests - passed);
|
||||
}
|
||||
|
||||
/*
|
||||
* 打印结果:
|
||||
* public static void com.jueee.item39.Item39Example02.m3() failed: java.lang.RuntimeException: Boom
|
||||
* public static void com.jueee.item39.Item39Example02.m7() failed: java.lang.RuntimeException: Crash
|
||||
* Invalid @Item39Example01: public void com.jueee.item39.Item39Example02.m5()
|
||||
* Passed: 1, Failed: 3
|
||||
*/
|
||||
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.jueee.item39;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Item39Example04 {
|
||||
|
||||
Class<? extends Throwable> value();
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package com.jueee.item39;
|
||||
|
||||
public class Item39Example05 {
|
||||
|
||||
@Item39Example04(ArithmeticException.class)
|
||||
public static void m1() { // Test should pass
|
||||
int i = 0;
|
||||
i = i / i;
|
||||
}
|
||||
|
||||
@Item39Example04(ArithmeticException.class)
|
||||
public static void m2() { // Should fail (wrong exception)
|
||||
int[] a = new int[0];
|
||||
int i = a[1];
|
||||
}
|
||||
|
||||
@Item39Example04(ArithmeticException.class)
|
||||
public static void m3() {
|
||||
} // Should fail (no exception)
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package com.jueee.item39;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class Item39Example06 {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String testClassName = "com.jueee.item39.Item39Example05";
|
||||
|
||||
int tests = 0;
|
||||
int passed = 0;
|
||||
Class<?> testClass = Class.forName(testClassName);
|
||||
for (Method m : testClass.getDeclaredMethods()) {
|
||||
if (m.isAnnotationPresent(Item39Example04.class)) {
|
||||
tests++;
|
||||
try {
|
||||
m.invoke(null);
|
||||
System.out.printf("Test %s failed: no exception%n", m);
|
||||
} catch (InvocationTargetException wrappedEx) {
|
||||
Throwable exc = wrappedEx.getCause();
|
||||
Class<? extends Throwable> excType = m.getAnnotation(Item39Example04.class).value();
|
||||
if (excType.isInstance(exc)) {
|
||||
passed++;
|
||||
} else {
|
||||
System.out.printf("Test %s failed: expected %s, got %s%n", m, excType.getName(), exc);
|
||||
}
|
||||
} catch (Exception exc) {
|
||||
System.out.println("Invalid @Test: " + m);
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.printf("Passed: %d, Failed: %d%n", passed, tests - passed);
|
||||
}
|
||||
|
||||
/*
|
||||
* 打印结果:
|
||||
* Test public static void com.jueee.item39.Item39Example05.m3() failed: no exception
|
||||
* Test public static void com.jueee.item39.Item39Example05.m2() failed: expected java.lang.ArithmeticException, got java.lang.ArrayIndexOutOfBoundsException: 1
|
||||
* Passed: 1, Failed: 2
|
||||
*/
|
||||
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.jueee.item39;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Repeatable;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
//Repeatable annotation type
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@Repeatable(Item39Example08.class)
|
||||
public @interface Item39Example07 {
|
||||
|
||||
Class<? extends Throwable> value();
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.jueee.item39;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Item39Example08 {
|
||||
|
||||
Item39Example07[] value();
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package com.jueee.item39;
|
||||
|
||||
public class Item39Example09 {
|
||||
|
||||
// Code containing a repeated annotation
|
||||
@Item39Example07(IndexOutOfBoundsException.class)
|
||||
@Item39Example07(NullPointerException.class)
|
||||
public static void doublyBad() {
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package com.jueee.item39;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class Item39Example10 {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String testClassName = "com.jueee.item39.Item39Example09";
|
||||
|
||||
int tests = 0;
|
||||
int passed = 0;
|
||||
Class<?> testClass = Class.forName(testClassName);
|
||||
for (Method m : testClass.getDeclaredMethods()) {
|
||||
if (m.isAnnotationPresent(Item39Example07.class) || m.isAnnotationPresent(Item39Example08.class)) {
|
||||
tests++;
|
||||
try {
|
||||
m.invoke(null);
|
||||
System.out.printf("Test %s failed: no exception%n", m);
|
||||
} catch (Throwable wrappedExc) {
|
||||
Throwable exc = wrappedExc.getCause();
|
||||
int oldPassed = passed;
|
||||
Item39Example07[] excTests = m.getAnnotationsByType(Item39Example07.class);
|
||||
for (Item39Example07 excTest : excTests) {
|
||||
if (excTest.value().isInstance(exc)) {
|
||||
passed++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (passed == oldPassed) {
|
||||
System.out.printf("Test %s failed: %s %n", m, exc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.printf("Passed: %d, Failed: %d%n", passed, tests - passed);
|
||||
}
|
||||
|
||||
/*
|
||||
* 打印结果:
|
||||
* Test public static void com.jueee.item39.Item39Example09.doublyBad() failed: no exception
|
||||
* Passed: 0, Failed: 1
|
||||
*/
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package com.jueee.item40;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Can you spot the bug?
|
||||
* @author hzweiyongqiang
|
||||
*/
|
||||
public class Item40Example01 {
|
||||
|
||||
private final char first;
|
||||
private final char second;
|
||||
|
||||
public Item40Example01(char first, char second) {
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
}
|
||||
|
||||
public boolean equals(Item40Example01 b) {
|
||||
return b.first == first && b.second == second;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return 31 * first + second;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
Set<Item40Example01> s = new HashSet<>();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (char ch = 'a'; ch <= 'z'; ch++) {
|
||||
s.add(new Item40Example01(ch, ch));
|
||||
}
|
||||
}
|
||||
System.out.println(s.size()); // 260
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package com.jueee.item40;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author hzweiyongqiang
|
||||
*/
|
||||
public class Item40Example02 {
|
||||
|
||||
private final char first;
|
||||
private final char second;
|
||||
|
||||
public Item40Example02(char first, char second) {
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof Item40Example02)) {
|
||||
return false;
|
||||
}
|
||||
Item40Example02 b = (Item40Example02) o;
|
||||
return b.first == first && b.second == second;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * first + second;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
Set<Item40Example02> s = new HashSet<>();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (char ch = 'a'; ch <= 'z'; ch++) {
|
||||
s.add(new Item40Example02(ch, ch));
|
||||
}
|
||||
}
|
||||
System.out.println(s.size()); // 26
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package com.jueee.item41;
|
||||
|
||||
/**
|
||||
* 使用标记接口定义类型
|
||||
* @author hzweiyongqiang
|
||||
*/
|
||||
public class Item41Example01 {
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user