Modern Java Recipes

Modern Java Recipes

Java之所以被開發,是要達到以下五個目的:

應當使用物件導向程式設計方法學
應當允許同一程式在不同的電腦平台執行
應當包括內建的對電腦網路的支援
應當被設計成安全地執行遠端程式碼
應當易於使用,並借鑑以前那些物件導向語言(如C++)的長處。

Java 在各個版本的演進
1.0-1.4 中的 java.lang.Thread
5.0 中的 java.util.concurrent
6.0 中的 Phasers …


This content originally appeared on DEV Community and was authored by DEV Community

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 模組化
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();

lambda 於建構子中

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

lambda 賦與變數

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

lambda 實做 FilenameFilter

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

lambda 區塊

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

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

// 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

在介面中提供實做

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 內

// 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 方法取得值

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

// 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

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

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

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

CHAPTER 4 Comparators and Collectors

java7 使用 collections

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

java8 透過 streams 使用 collections

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

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

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

在 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()

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

檢查 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());

透過 @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());

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

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

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

列出目錄檔案清單

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

找出含有特定目錄的檔案

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 並建議之後的使用者使用它做為開發。

基本使用

// 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 互換

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 來進行並發任務

並行範例

// 下列陣列將不會按順訊印出
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 與其他方式比較

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) 計算效能

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 參數

// 改變 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

等待未來的結果

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

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

介面私有成員

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.

@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

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

日期計算

// 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 的物件

// 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.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)

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

簡易泛型代碼

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 所定義的方法

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> 並沒有關連

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

未只定 List 的通配符

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

Upper Bounded Wildcards

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

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

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

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

多重分配符

T extends Runnable & AutoCloseable


This content originally appeared on DEV Community and was authored by DEV Community


Print Share Comment Cite Upload Translate Updates
APA

DEV Community | Sciencx (2022-03-01T06:20:44+00:00) Modern Java Recipes. Retrieved from https://www.scien.cx/2022/03/01/modern-java-recipes/

MLA
" » Modern Java Recipes." DEV Community | Sciencx - Tuesday March 1, 2022, https://www.scien.cx/2022/03/01/modern-java-recipes/
HARVARD
DEV Community | Sciencx Tuesday March 1, 2022 » Modern Java Recipes., viewed ,<https://www.scien.cx/2022/03/01/modern-java-recipes/>
VANCOUVER
DEV Community | Sciencx - » Modern Java Recipes. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2022/03/01/modern-java-recipes/
CHICAGO
" » Modern Java Recipes." DEV Community | Sciencx - Accessed . https://www.scien.cx/2022/03/01/modern-java-recipes/
IEEE
" » Modern Java Recipes." DEV Community | Sciencx [Online]. Available: https://www.scien.cx/2022/03/01/modern-java-recipes/. [Accessed: ]
rf:citation
» Modern Java Recipes | DEV Community | Sciencx | https://www.scien.cx/2022/03/01/modern-java-recipes/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.