Modern Java Recipes
Tags: review, java
Published: 2022-03-01 06:20:44

---
title: Modern Java Recipes
tags: Review, java
author: Corey Lai
---

**Java之所以被開發，是要達到以下五個目的：**
-   應當使用物件導向程式設計方法學
-   應當允許同一程式在不同的電腦平台執行
-   應當包括內建的對電腦網路的支援
-   應當被設計成安全地執行遠端程式碼
-   應當易於使用，並借鑑以前那些物件導向語言（如C++）的長處。

**Java 在各個版本的演進**
1.0-1.4 中的 java.lang.Thread
5.0 中的 java.util.concurrent
6.0 中的 Phasers 等
7.0 中的 Fork/Join 框架
8.0 中的 Lambda、Stream
9.0 中的 Jigsaw 模組化

**Developer Skills Tree**
http://aliyunzixunbucket.oss-cn-beijing.aliyuncs.com/jpg/87a7e437be53abaa0761f7a462a7da69.jpg?x-oss-process=image/resize,p_100/auto-orient,1/quality,q_90/format,jpg/watermark,image_eXVuY2VzaGk=,t_100,g_se,x_0,y_0

# CHAPTER 1 Basic

**Anonymous inner - > Lambda**

**傳統匿名語法**
```java
new Thread(new Runnable() {
	@Override
	public void run() {
		System.out.println("inside runnable using an anonymous inner class");
	}
}).start();
```
**lambda 於建構子中**
```java
new Thread(() -> System.out.println("inside Thread constructor using lambda")).start();
```
**lambda 賦與變數**
```java
Runnable r = () -> System.out.println("lambda expression implementing the run method");
```
**lambda 實做 FilenameFilter**
```java
String[] names = directory.list((dir, name) -> name.endsWith(".java"));
System.out.println(Arrays.asList(names));
```
**lambda 區塊**
```java
String[] names = directory.list((File dir, String name) -> {
return name.endsWith(".java");
});
```
**使用 方法參考(method reference) 存取 print**
```java
// Consumer<Integer>  functional interface
Consumer<Integer> printer = System.out::println;
Stream.of(3, 1, 4, 1, 5, 9).forEach(printer);
```

**語句**
object::instanceMethod
物件實例方法 ex: System.out::println
Class::staticMethod
類別靜態方法 ex: Math::max
Class::instanceMethod
類別實例方法 ex: String::length

## @FunctionalInterface
註釋該介面目標為 lambda expression or method reference.
只能有一個**抽象**成員

## Default Methods
在介面中提供實做
```java
public interface Collection<E> extends Iterable<E> {
	boolean isEmpty();
	default boolean removeIf(Predicate<? super E> filter) {  
	    Objects.requireNonNull(filter);  
		boolean removed = false;  
		 final Iterator<E> each = iterator();  
		 while (each.hasNext()) {  
			if (filter.test(each.next())) {  
	            each.remove();  
				removed = true;  
			}  
	     }  
	    return removed;  
	}
}

// usage 
boolean removed = Arrays.asList(3, 1, 4, 1, 5, 9).removeIf(n -> n <= 0);
```

# CHAPTER 2 The java.util.function Package
**p.s. ReferencePipeline內的 downstream 為 Consumer 類**
## Consumer
將欲使用方法傳入 Consumer 內
```java
// implement
public interface Iterable<T> {  
	Iterator<T> iterator();  
	default void forEach(Consumer<? super T> action) {  
		Objects.requireNonNull(action);  
		for (T t : this) {  
			action.accept(t);  
		}  
	}
}

// usage
Arrays.asList("this", "is", "a", "list", "of", "strings").forEach(System.out::println);
```

## Suppliers
將產生的值載入 supplier 內 並透過 get 方法取得值
```java
Supplier<T> sippiler = () -> T;
DoubleSupplier randomSupplier = Math::random;
// 此時才呼叫 Math.random()
System.out.println(randomSupplier.getDouble());
```
附加 Suppliers 介面

|Interface |abstract method|
|--|--|
|Supplier|T get()|
|IntSupplier|int getAsInt()|
|DoubleSupplier|double getAsDouble()|
|LongSupplier|long getAsLong()|
|BooleanSupplier|boolean getAsBoolean()|

## Predicates
透過 Predicates 判斷是否有符合 filter
```java
// implement
public static boolean predicateTest(int value, Predicate<Integer> predicate) {  
    return predicate.test(value);  
}
// usage
predicateTest(3, (x) -> x == 3);

// Stream implement
Stream<T> filter(Predicate<? super T> predicate);
abstract class IntPipeline<E_IN>  
	extends AbstractPipeline<E_IN, Integer, IntStream>  
	implements IntStream {
	@Override  
	public final IntStream filter(IntPredicate predicate) {  
		Objects.requireNonNull(predicate);  
		return new StatelessOp<Integer>(this, StreamShape.INT_VALUE,  
		StreamOpFlag.NOT_SIZED) {  
				@Override  
				Sink<Integer> opWrapSink(int flags, Sink<Integer> sink) {  
				return new Sink.ChainedInt<Integer>(sink) {  
					@Override  
					public void begin(long size) {  
						downstream.begin(-1);  
					}  

					@Override  
					public void accept(int t) {  
					    // 檢查是否通過測試
						if (predicate.test(t))  
							downstream.accept(t);  
					}  
				};  
			}  
		};  
	}
}

// usage
Arrays.asList("this", "is", "a", "list", "of", "strings")
	.stream()
	.filter(s -> s.length() == 2)
	.collect(Collectors.joining(", "));
```

## Functions
呼叫特定方法，並回傳所需內容
```java
// implement
static int modifyTheValue(int valueToBeOperated, Function<Integer, Integer> function) {  
    return function.apply(valueToBeOperated);  
}
// usage
modifyTheValue(10, (x)-> x + 20);

// Stream implement
abstract class ReferencePipeline<P_IN, P_OUT>  
        extends AbstractPipeline<P_IN, P_OUT, Stream<P_OUT>>  
        implements Stream<P_OUT>  {
	@Override  
	@SuppressWarnings("unchecked")  
	public final <R> Stream<R> map(Function<? super P_OUT, ? extends R> mapper) {  
		Objects.requireNonNull(mapper);  
		return new StatelessOp<P_OUT, R>(this, StreamShape.REFERENCE,  
			StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) {  
				@Override  
				Sink<P_OUT> opWrapSink(int flags, Sink<R> sink) {  
				return new Sink.ChainedReference<P_OUT, R>(sink) {  
					@Override  
					public void accept(P_OUT u) {  
						downstream.accept(mapper.apply(u));  
					}  
				};  
			}  
		};  
	}
}

// usage
Arrays.asList("this", "is", "a", "list", "of", "strings")
	.stream()
	.map(String::length)
	.collect(Collectors.toList());
```
附加 Functions 介面

|Interface|abstract method|
|--|--|
|Function|R apply(t value)|
|IntFunction|R apply(int value)|
|DoubleFunction|R apply(double value)|
|LongFunction|R apply(long value)|
|ToIntFunction|int applyAsInt(T value)|
|ToDoubleFunction|double applyAsDouble(T value)|
|ToLongFunction|long applyAsLong(T value)|
|DoubleToIntFunction|int applyAsInt(double value)|
|DoubleToLongFunction|long applyAsLong(double value)|
|IntToDoubleFunction|double applyAsDouble(int value)|
|IntToLongFunction|long applyAsLong(int value)|
|LongToDoubleFunction|double applyAsDouble(long value)|
|LongToIntFunction|int applyAsInt(long value)|
|BiFunction|void accept(T t, U u)|
|ToIntBiFunction|int applyAsInt(T t, U u)|
|ToDoubleBiFunction|double applyAsDouble(T t, U u)|
|ToLongBiFunction|long applyAsLong(T t, U u)|


# CHAPTER 3 Streams

java 8 新串流 API 支援 functional programming，串流只能被使用一次，如需要再次處理數據必須再重新建立。

### Stream 運行原理
```java
// 呼叫流程
Arrays.stream -> StreamSupport.stream -> return ReferencePipeline.Head -> filter (or the method you need)
```

# CHAPTER 4 Comparators and Collectors

### java7 使用 collections
```java
public List<String> defaultSort() {
	Collections.sort(list);
	return sampleStrings;
}
```

### java8 透過 streams 使用 collections
```java
public List<String> defaultSortUsingStreams() {
	return list.stream()
	.sorted()
	.collect(Collectors.toList());
}
```

### collect 方法 兩種實做
```java
<R, A> R collect(Collector<? super T, A, R> collector);
<R> R collect(Supplier<R> supplier,  
  BiConsumer<R, ? super T> accumulator,  
  BiConsumer<R, R> combiner);
```

# CHAPTER 5 Issues with Streams, Lambdas, and Method References

### 使用靜態方法確認空值與比較
```java 
// 去除空值資料
List<String> nonNullStrings = Arrays.asList(
"this", null, "is", "a", null, "list", "of", "strings", null)
.stream().filter(Objects::nonNull).collect(Collectors.toList());
```

### 在 lambdas 內存取在外 local 變數
```java
int total = 0;
// 錯誤 lambdas 無法存取 local var
Arrays.asList(3, 1, 4, 1, 5, 9).forEach(n -> total += n);

// 使用 function 傳入屬性進行存取
Arrays.asList(3, 1, 4, 1, 5, 9)
.stream().mapToInt(Integer::valueOf).sum()
```

### 使用 forEach 來進行迭代
```java
List<Integer> integers = Arrays.asList(3, 1, 4, 1, 5, 9);
// 匿名方法
integers.forEach(new Consumer<Integer>() {
	@Override
	public void accept(Integer integer) {
		System.out.println(integer);
	}
});
// 完整敘述式 lambda
integers.forEach((Integer n) -> {System.out.println(n);});
// 表達式 lambda
integers.forEach(n -> System.out.println(n));
// 方法參照
integers.forEach(System.out::println);
```

### 檢查 lambda 錯誤 exception 
```java
// 使用 encode 必須處理 UnsupportedEncodingException
Arrays.stream(values).map(s -> URLEncoder.encode(s, "UTF-8"))).collect(Collectors.toList());
// solution
Arrays.stream(values)
.map(s -> {
	try {
		return URLEncoder.encode(s, "UTF-8");
	} catch (UnsupportedEncodingException e) {
		e.printStackTrace();
		return "";
	}
})
.collect(Collectors.toList());
```

### 透過 @FunctionalInterface 改寫
```java
@FunctionalInterface
public interface FunctionWithException<T, R, E extends Exception> {
	R apply(T t) throws E;
}

private static <T, R, E extends Exception> Function<T, R> wrapper(FunctionWithException<T, R, E> fe) {
		return arg -> {
			try {
				return fe.apply(arg);
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		};
}

Arrays.stream(values).map(wrapper(s -> URLEncoder.encode(s, "UTF-8"))).collect(Collectors.toList());
```

# CHAPTER 6 The Optional Type

Java 8 新的 `java.util.Optional<T>` 類，目的是解決許多開發者困擾的 NullPointerExceptions，他被設計以溝通的方式告知回傳值為不合法的空值。

### Optional 結合 AtomicInteger 範例

```java
AtomicInteger counter = new AtomicInteger();
Optional<AtomicInteger> opt = Optional.ofNullable(counter);
System.out.println(optional); // Optional[0]
counter.incrementAndGet();
System.out.println(optional); // Optional[1]
optional.get().incrementAndGet();
// 重新指定內容
optional = Optional.ofNullable(new AtomicInteger());
```

### Optional  例外範例
```java
Optional<String> firstOdd =
Stream.of("five", "even", "length", "string", "values")
.filter(s -> s.length() % 2 != 0)
.findFirst();
// throws NoSuchElementException
System.out.println(firstOdd.get()); 
// 使用 isPresent 檢查空值
System.out.println(
firstOdd.isPresent() ? firstOdd.get() : "No even length strings");
// 如值為空回傳預設值
firstOdd.orElse("No even length strings");
// 如值為空執行Methods並回傳值
firstOdd.orElseGet(() -> new String());
```

# CHAPTER 7 File I/O

使用 stream 處理檔案內文

### 找出檔案內最長的單字前10名
```java
try (Stream<String> lines = Files.lines(Paths.get("/usr/share/dict/web2")) {
	lines.filter(s -> s.length() > 20)
	.sorted(Comparator.comparingInt(String::length).reversed()).limit(10)
	.forEach(w -> System.out.printf("%s (%d)%n", w, w.length()));
} catch (IOException e) {
	e.printStackTrace();
}
```
### 列出目錄檔案清單
```java
try (Stream<Path> paths = Files.walk(Paths.get("src/main/java"))) {
	paths.forEach(System.out::println);
} catch (IOException e) {
	e.printStackTrace();
}
```
### 找出含有特定目錄的檔案
```java 
try (Stream<Path> paths =
Files.find(Paths.get("src/main/java"), Integer.MAX_VALUE, (path, attributes) -> !attributes.isDirectory() && path.toString().contains("fileio"))) {
	paths.forEach(System.out::println);
} catch (IOException e) {
	e.printStackTrace();
}
```


# CHAPTER 8 The java.time Package

從 JDK1.0 開始就包含的程式庫，java.util.Date  ，  大多數 java.util.Date 方法都被棄用並使用 java.util.Calendar 來取代，但也並不是那麼方便取用。

終於在 Java 8 後導入了 Joda-Time library 並建議之後的使用者使用它做為開發。

### 基本使用
```java
// Instant.now(): 2017-06-20T17:27:08.184Z
System.out.println("Instant.now(): " + Instant.now());
// LocalDate.now(): 2017-06-20
System.out.println("LocalDate.now(): " + LocalDate.now());
// LocalTime.now(): 13:27:08.318
System.out.println("LocalTime.now(): " + LocalTime.now());
// LocalDateTime.now(): 2017-06-20T13:27:08.319
System.out.println("LocalDateTime.now(): " + LocalDateTime.now());
// ZonedDateTime.now(): 2017-06-20T13:27:08.319-04:00[America/New_York]
System.out.println("ZonedDateTime.now(): " + ZonedDateTime.now());
```
###  util.Date, util.Calendar, java.sql.Date 與 util.time 互換
```java
package datetime;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;
public class ConvertDate {
	public LocalDate convertFromSqlDatetoLD(java.sql.Date sqlDate) {
		return sqlDate.toLocalDate();
	}
	public java.sql.Date convertToSqlDateFromLD(LocalDate localDate) {
		return java.sql.Date.valueOf(localDate);
	}
	public LocalDateTime convertFromTimestampToLDT(Timestamp timestamp) {
		return timestamp.toLocalDateTime();
	}
	public Timestamp convertToTimestampFromLDT(LocalDateTime localDateTime) {
		return Timestamp.valueOf(localDateTime);
	}
}
```

# CHAPTER 9 Parallelism and Concurrency

parallelStream 是透過 ForkJoinPool Thread 來進行並發任務

### 並行範例
```java 
// 下列陣列將不會按順訊印出
Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9)
.parallelStream().forEach(out::println);  
// 使用 forEachOrdered 強制以順序印出
Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9)
.parallelStream().forEachOrdered(out::println);
```

### Parallelism 與其他方式比較
```java
class Person {
        int    id;
        String name;
        String sex;
        float  height;
}

// 傳統迴圈
public List<Person> constructPersons() {
    List<Person> persons = new ArrayList<Person>();
    for (int i = 0; i < 5; i++) {
        Person p = new Person(i, "name" + i, "sex" + i, i);
        persons.add(p);
    }
    return persons;
}

// 迴圈
public void doFor(List<Person> persons) {
    long start = System.currentTimeMillis();
    for (Person p : persons) {
        System.out.println(p.name);
    }
    long end = System.currentTimeMillis();
    System.out.println("doFor cost:" + (end - start));
}

// 串流
public void doStream(List<Person> persons) {
    long start = System.currentTimeMillis();

    persons.stream().forEach(x -> System.out.println(x.name));

    long end = System.currentTimeMillis();
    System.out.println("doStream cost:" + (end - start));
}

// 並行
public void doParallelStream(List<Person> persons) {
    long start = System.currentTimeMillis();
    persons.parallelStream().forEach(x -> System.out.println(x.name));
    long end = System.currentTimeMillis();
    System.out.println("doParallelStream cost:" + (end - start));
}
```

### 使用 openjdk (Java Micro-benchmark Harness) 計算效能 
```java
import org.openjdk.jmh.annotations.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Thread)
@Fork(value = 2, jvmArgs = {"-Xms4G", "-Xmx4G"})
public class DoublingDemo {
	public int doubleIt(int n) {
		try {
			Thread.sleep(100);
		} catch (InterruptedException ignored) { }
		return n * 2;
	}

	@Benchmark
	public int doubleAndSumSequential() {
		return IntStream.of(3, 1, 4, 1, 5, 9).map(this::doubleIt).sum();
	}

	@Benchmark
	public int doubleAndSumParallel() {
		return IntStream.of(3, 1, 4, 1, 5, 9)
		.parallel().map(this::doubleIt).sum();
	}
}
```

### ForkJoinPool
改變 ForkJoinPool 參數
```java
// 改變 pool 大小
System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "20");
// 使用自定的 ForkJoinPool 執行 parallel
ForkJoinPool pool = new ForkJoinPool(15);
ForkJoinTask<Long> task = pool.submit(() -> LongStream.rangeClosed(1, 3_000_000).parallel().sum());
```
### Future
等待未來的結果
```java
public void getIfNotCancelled(Future<String> future) {
	if (!future.isCancelled()) {
		System.out.println(future.get());
	} else {
		System.out.println("Cancelled");
	}
}

ExecutorService service = Executors.newCachedThreadPool();
Future<String> future = service.submit(new Callable<String>() {
	@Override
	public String call() throws Exception {
		Thread.sleep(100);
		return "Hello, World!";
	}
});
// 取消 service submit
future.cancel(true);
System.out.println("Processing...");
getIfNotCancelled(future);
```

# CHAPTER 10 Java 9 Additions

Java 9 新功能

### Jigsaw
Suppliers Module 
```java
package com.oreilly.suppliers;
public class NamesSupplier implements Supplier<Stream<String>> {
	private Path namesPath = Paths.get("server/src/main/resources/names.txt");
	@Override
	public Stream<String> get() {
		try {
			return Files.lines(namesPath);
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
	}
}
// module-info.java
module com.oreilly.suppliers {
	exports com.oreilly.suppliers;
}
```
```java
package com.kousenit.clients;
import com.oreilly.suppliers.NamesSupplier;
public class Main {
	public static void main(String[] args) throws IOException {
		NamesSupplier supplier = new NamesSupplier();
		try (Stream<String> lines = supplier.get()) {
			lines.forEach(line -> System.out.printf("Hello, %s!%n", line));
		}
	}
}
// module-info.java
module com.kousenit.clients {
	requires com.oreilly.suppliers;
}
```

### 介面私有成員
```java
public interface SumNumbers {
	default int addEvens(int... nums) {
		return add(n -> n % 2 == 0, nums);
	}
	default int addOdds(int... nums) {
		return add(n -> n % 2 != 0, nums);
	}
	private int add(IntPredicate predicate, int... nums) {
		return IntStream.of(nums)
		.filter(predicate)
		.sum();
	}
}
class PrivateDemo implements SumNumbers {}
public class SumNumbersTest {
	private SumNumbers demo = new PrivateDemo();
	@Test
	public void addEvens() throws Exception {
		assertEquals(2 + 4 + 6, demo.addEvens(1, 2, 3, 4, 5, 6));
	}
	@Test
	public void addOdds() throws Exception {
		assertEquals(1 + 3 + 5, demo.addOdds(1, 2, 3, 4, 5, 6));
	}
}
```

### 不可變動集合
靜態工廠　
不可變動集合 使用 add, addAll, clear, remove, removeAll, replaceAll, set  將會拋出 UnsupportedOperationException.
```java
@Test(expected = UnsupportedOperationException.class)
public void showImmutabilityAdd() throws Exception {
	List<Integer> intList = List.of(1, 2, 3);
	intList.add(99);
}
@Test(expected = UnsupportedOperationException.class)
public void showImmutabilityClear() throws Exception {
	List<Integer> intList = List.of(1, 2, 3);
	intList.clear();
}
@Test(expected = UnsupportedOperationException.class)
public void showImmutabilityRemove() throws Exception {
	List<Integer> intList = List.of(1, 2, 3);
	intList.remove(0);
}
@Test(expected = UnsupportedOperationException.class)
public void showImmutabilityReplace() throws Exception {
	List<Integer> intList = List.of(1, 2, 3);
	intList.replaceAll(n -> -n);
}
@Test(expected = UnsupportedOperationException.class)
public void showImmutabilitySet() throws Exception {
	List<Integer> intList = List.of(1, 2, 3);
	intList.set(0, 99);
}

// 如果將不可變動集合塞入物件，將可改變物件內容
@Test
public void areWeImmutableOrArentWe() throws Exception {
	List<Holder> holders = List.of(new Holder(1), new Holder(2));
	assertEquals(1, holders.get(0).getX());
	holders.get(0).setX(4);
	assertEquals(4, holders.get(0).getX());
}
```

### 迭代使用Predicate 
```java
// Java8 方法
List<BigDecimal> bigDecimals =
Stream.iterate(BigDecimal.ZERO, bd -> bd.add(BigDecimal.ONE)).limit(10).collect(Collectors.toList());

// Java9 方法
bigDecimals = Stream.iterate(BigDecimal.ZERO,
bd -> bd.longValue() < 10L,
bd -> bd.add(BigDecimal.ONE))
.collect(Collectors.toList());
```

### 日期計算
```java
// Java8 用法
public List<LocalDate> getDays_java8(LocalDate start, LocalDate end) {
	Period period = start.until(end);
	return LongStream.range(0, ChronoUnit.DAYS.between(start, end))
	.mapToObj(start:plusDays)
	.collect(Collectors.toList());
}
LocalDate start = LocalDate.of(2017, Month.JUNE, 10);
LocalDate end = LocalDate.of(2017, Month.JUNE, 17);
System.out.println(dateRange.getDays_java8(start, end));

// Java9 用法
public List<LocalDate> getDays_java9(LocalDate start, LocalDate end) {
	return start.datesUntil(end).collect(Collectors.toList());
}
public List<LocalDate> getMonths_java9(LocalDate start, LocalDate end) {
	return start.datesUntil(end, Period.ofMonths(1)).collect(Collectors.toList());
}
```

## Extra Information
###  Try-With-Resources
Try with Resources 會自動幫開發者關閉 try(closeable; ... ) 內實做 java.lang.AutoCloseable 的物件
```java
// Java8 用法
// inputstream
try(InputStream stream1 = new InputStream(....); InputStream stream2 = new InputStream(....)) { ... }
// connection
try (Connection con = DriverManager.getConnection(myConnectionURL);
      PreparedStatement ps = createPreparedStatement(con, userId); 
      ResultSet rs = ps.executeQuery()) {
      // process the resultset here, all resources will be cleaned up
 } catch (Exception e) {
     e.printStackTrace();
 }

// Java9 用法
InputStream stream1 =  new  InputStream(....);  InputStream stream2 =  new  InputStream(....);  ....  try(stream1;stream2){  ....  }
```


# APPENDIX A Generics and Java 8
關於泛型的能力大多數的 Java 開發者都只認識到他們所需用於工作上的範圍，跟著 Java 8 的來到 javadoc 文件已經充斥著像是以下的代碼 :
```java
// java.util.Map.Entry
static <K extends Comparable<? super K>,V> Comparator<Map.Entry<K,V>>
comparingByKey()
// or this one from java.util.Comparator:
static <T,U extends Comparable<? super U>> Comparator<T> comparing(
Function<? super T,? extends U> keyExtractor)
// or even this monster from java.util.stream.Collectors:
static <T,K,D,A,M extends Map<K, D>> Collector<T,?,M> groupingBy(
Function<? super T,? extends K> classifier, Supplier<M> mapFactory,
Collector<? super T,A,D> downstream)
```
理解基礎的泛型已不再夠用。

### 簡易泛型代碼
```java
List<String> strings = new ArrayList<>();
strings.add("Hello");
strings.add("World");
// strings.add(new Date());
// Integer i = strings.get(0);  編譯不過
for (String s : strings) {
	System.out.printf("%s has length %d%n", s, s.length());
}
```
### List 所定義的方法
```java
boolean add(E e)
boolean addAll(Collection<? extends E> c)
void clear()
boolean contains(Object o)
boolean containsAll(Collection<?> c)
E get(int index)
```
### 有些開發者所不理解的
許多開發者對於 ArrayList`<String>` 跟 ArrayList`<Object>` 並沒有關連
```java
List<String> strings = new ArrayList<>();
String s = "abc";
String s = "abc";
Object o = s;
// 這樣的代碼是不被允許的
strings.add(o);
```
### 未只定 List 的通配符
```java
List<?> stuff = new ArrayList<>();
// 未指定泛型的 list 不能被寫入
stuff.add("abc");
stuff.add(new Object());
stuff.add(3);
// 但是能被讀取
int numElements = stuff.size();
```
### Upper Bounded Wildcards
使用向上通配符一樣不能被寫入
```java
List<? extends Number> numbers = new ArrayList<>();
// numbers.add(3);
// numbers.add(3.14159);
// numbers.add(new BigDecimal("3"));
```
### 使用 Upper Bounded 
```java 
private static double sumList(List<? extends Number> list) {
	return list.stream().mapToDouble(Number::doubleValue).sum();
}

List<Integer> ints = Arrays.asList(1, 2, 3, 4, 5);
List<Double> doubles = Arrays.asList(1.0, 2.0, 3.0, 4.0, 5.0);
List<BigDecimal> bigDecimals = Arrays.asList(
new BigDecimal("1.0"),new BigDecimal("2.0"),new BigDecimal("3.0"),new BigDecimal("4.0"),new BigDecimal("5.0"));
System.out.printf("ints sum is %s%n", sumList(ints));
System.out.printf("doubles sum is %s%n", sumList(doubles));
System.out.printf("big decimals sum is %s%n", sumList(bigDecimals));

```

### 使用 Lower Bounded
向下通配符一樣不能被寫入
```java 
public void numsUpTo(Integer num, List<? super Integer> output) {
	IntStream.rangeClosed(1, num).forEach(output::add);
}

ArrayList<Integer> integerList = new ArrayList<>();
ArrayList<Number> numberList = new ArrayList<>();
ArrayList<Object> objectList = new ArrayList<>();
numsUpTo(5, integerList);
numsUpTo(5, numberList);
numsUpTo(5, objectList);
```

### 多重分配符
```java
T extends Runnable & AutoCloseable
```