DEV Community

HyperRedStart
HyperRedStart

Posted on • Edited on

Modern Java Recipes

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

傳統匿名語法

new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("inside runnable using an anonymous inner class");
    }
}).start();
Enter fullscreen mode Exit fullscreen mode

lambda 於建構子中

new Thread(() -> System.out.println("inside Thread constructor using lambda")).start();
Enter fullscreen mode Exit fullscreen mode

lambda 賦與變數

Runnable r = () -> System.out.println("lambda expression implementing the run method");
Enter fullscreen mode Exit fullscreen mode

lambda 實做 FilenameFilter

String[] names = directory.list((dir, name) -> name.endsWith(".java"));
System.out.println(Arrays.asList(names));
Enter fullscreen mode Exit fullscreen mode

lambda 區塊

String[] names = directory.list((File dir, String name) -> {
return name.endsWith(".java");
});
Enter fullscreen mode Exit fullscreen mode

使用 方法參考(method reference) 存取 print

// Consumer<Integer>  functional interface
Consumer<Integer> printer = System.out::println;
Stream.of(3, 1, 4, 1, 5, 9).forEach(printer);
Enter fullscreen mode Exit fullscreen mode

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

@FunctionalInterface

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

Default Methods

在介面中提供實做

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);
Enter fullscreen mode Exit fullscreen mode

CHAPTER 2 The java.util.function Package

p.s. ReferencePipeline內的 downstream 為 Consumer 類

Consumer

將欲使用方法傳入 Consumer 內

// 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);
Enter fullscreen mode Exit fullscreen mode

Suppliers

將產生的值載入 supplier 內 並透過 get 方法取得值

Supplier<T> sippiler = () -> T;
DoubleSupplier randomSupplier = Math::random;
// 此時才呼叫 Math.random()
System.out.println(randomSupplier.getDouble());
Enter fullscreen mode Exit fullscreen mode

附加 Suppliers 介面

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

Predicates

透過 Predicates 判斷是否有符合 filter

// 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(", "));
Enter fullscreen mode Exit fullscreen mode

Functions

呼叫特定方法,並回傳所需內容

// 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());
Enter fullscreen mode Exit fullscreen mode

附加 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 運行原理

// 呼叫流程
Arrays.stream -> StreamSupport.stream -> return ReferencePipeline.Head -> filter (or the method you need)
Enter fullscreen mode Exit fullscreen mode

CHAPTER 4 Comparators and Collectors

java7 使用 collections

public List<String> defaultSort() {
    Collections.sort(list);
    return sampleStrings;
}
Enter fullscreen mode Exit fullscreen mode

java8 透過 streams 使用 collections

public List<String> defaultSortUsingStreams() {
    return list.stream()
    .sorted()
    .collect(Collectors.toList());
}
Enter fullscreen mode Exit fullscreen mode

collect 方法 兩種實做

<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);
Enter fullscreen mode Exit fullscreen mode

CHAPTER 5 Issues with Streams, Lambdas, and Method References

使用靜態方法確認空值與比較

// 去除空值資料
List<String> nonNullStrings = Arrays.asList(
"this", null, "is", "a", null, "list", "of", "strings", null)
.stream().filter(Objects::nonNull).collect(Collectors.toList());
Enter fullscreen mode Exit fullscreen mode

在 lambdas 內存取在外 local 變數

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()
Enter fullscreen mode Exit fullscreen mode

使用 forEach 來進行迭代

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);
Enter fullscreen mode Exit fullscreen mode

檢查 lambda 錯誤 exception

// 使用 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());
Enter fullscreen mode Exit fullscreen mode

透過 @FunctionalInterface 改寫

@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());
Enter fullscreen mode Exit fullscreen mode

CHAPTER 6 The Optional Type

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

Optional 結合 AtomicInteger 範例

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());
Enter fullscreen mode Exit fullscreen mode

Optional 例外範例

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());
Enter fullscreen mode Exit fullscreen mode

CHAPTER 7 File I/O

使用 stream 處理檔案內文

找出檔案內最長的單字前10名

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();
}
Enter fullscreen mode Exit fullscreen mode

列出目錄檔案清單

try (Stream<Path> paths = Files.walk(Paths.get("src/main/java"))) {
    paths.forEach(System.out::println);
} catch (IOException e) {
    e.printStackTrace();
}
Enter fullscreen mode Exit fullscreen mode

找出含有特定目錄的檔案

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();
}
Enter fullscreen mode Exit fullscreen mode

CHAPTER 8 The java.time Package

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

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

基本使用

// 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());
Enter fullscreen mode Exit fullscreen mode

util.Date, util.Calendar, java.sql.Date 與 util.time 互換

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

CHAPTER 9 Parallelism and Concurrency

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

並行範例

// 下列陣列將不會按順訊印出
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);
Enter fullscreen mode Exit fullscreen mode

Parallelism 與其他方式比較

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));
}
Enter fullscreen mode Exit fullscreen mode

使用 openjdk (Java Micro-benchmark Harness) 計算效能

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();
    }
}
Enter fullscreen mode Exit fullscreen mode

ForkJoinPool

改變 ForkJoinPool 參數

// 改變 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());
Enter fullscreen mode Exit fullscreen mode

Future

等待未來的結果

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);
Enter fullscreen mode Exit fullscreen mode

CHAPTER 10 Java 9 Additions

Java 9 新功能

Jigsaw

Suppliers Module

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;
}
Enter fullscreen mode Exit fullscreen mode
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;
}
Enter fullscreen mode Exit fullscreen mode

介面私有成員

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));
    }
}
Enter fullscreen mode Exit fullscreen mode

不可變動集合

靜態工廠 
不可變動集合 使用 add, addAll, clear, remove, removeAll, replaceAll, set 將會拋出 UnsupportedOperationException.

@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());
}
Enter fullscreen mode Exit fullscreen mode

迭代使用Predicate

// 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());
Enter fullscreen mode Exit fullscreen mode

日期計算

// 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());
}
Enter fullscreen mode Exit fullscreen mode

Extra Information

Try-With-Resources

Try with Resources 會自動幫開發者關閉 try(closeable; ... ) 內實做 java.lang.AutoCloseable 的物件

// 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){  ....  }
Enter fullscreen mode Exit fullscreen mode

APPENDIX A Generics and Java 8

關於泛型的能力大多數的 Java 開發者都只認識到他們所需用於工作上的範圍,跟著 Java 8 的來到 javadoc 文件已經充斥著像是以下的代碼 :

// 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)
Enter fullscreen mode Exit fullscreen mode

理解基礎的泛型已不再夠用。

簡易泛型代碼

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());
}
Enter fullscreen mode Exit fullscreen mode

List 所定義的方法

boolean add(E e)
boolean addAll(Collection<? extends E> c)
void clear()
boolean contains(Object o)
boolean containsAll(Collection<?> c)
E get(int index)
Enter fullscreen mode Exit fullscreen mode

有些開發者所不理解的

許多開發者對於 ArrayList<String> 跟 ArrayList<Object> 並沒有關連

List<String> strings = new ArrayList<>();
String s = "abc";
String s = "abc";
Object o = s;
// 這樣的代碼是不被允許的
strings.add(o);
Enter fullscreen mode Exit fullscreen mode

未只定 List 的通配符

List<?> stuff = new ArrayList<>();
// 未指定泛型的 list 不能被寫入
stuff.add("abc");
stuff.add(new Object());
stuff.add(3);
// 但是能被讀取
int numElements = stuff.size();
Enter fullscreen mode Exit fullscreen mode

Upper Bounded Wildcards

使用向上通配符一樣不能被寫入

List<? extends Number> numbers = new ArrayList<>();
// numbers.add(3);
// numbers.add(3.14159);
// numbers.add(new BigDecimal("3"));
Enter fullscreen mode Exit fullscreen mode

使用 Upper Bounded

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));

Enter fullscreen mode Exit fullscreen mode

使用 Lower Bounded

向下通配符一樣不能被寫入

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);
Enter fullscreen mode Exit fullscreen mode

多重分配符

T extends Runnable & AutoCloseable
Enter fullscreen mode Exit fullscreen mode

Top comments (0)