Sitemap
Judy’s Effective Java sharing

Effective Java心得

Item 22: 偏好使用static member classes而不是nonstatic

11 min readAug 8, 2024

--

Nested class的四種類型

什麼是Nested class?

nested class是一種被宣告在class裡面的class,也就是所謂的inner classes,共有四種類型:static member classes、nonstatic member classes、anonymous classes和local classes。

➡ 什麼是static member class?

在nested class中,有static屬性的class,就是static member classes。下面範例,示範了如何撰寫static member class,可以看到Operation有static屬性,它的行為就像helper class一樣,不需要被實體化,就可以執行outer class(Calculator)需要它執行的功能。

因為static member classes有static屬性,所以實作static member class時,不能直接存取和使用enclosing class(outer class)的欄位和方法,就像範例中的Operation,完全沒有使用Calculator的欄位和方法,這個特性讓static member classes不只可以成為Nested class,也可以自己獨立出去。

public class Calculator {

// 靜態成員類別
public static class Operation {
public static final Operation PLUS = new Operation("+") {
@Override
public double apply(double x, double y) {
return x + y;
}
};

public static final Operation MINUS = new Operation("-") {
@Override
public double apply(double x, double y) {
return x - y;
}
};

public static final Operation MULTIPLY = new Operation("*") {
@Override
public double apply(double x, double y) {
return x * y;
}
};

public static final Operation DIVIDE = new Operation("/") {
@Override
public double apply(double x, double y) {
if (y == 0) throw new ArithmeticException("Division by zero");
return x / y;
}
};

private final String symbol;

private Operation(String symbol) {
this.symbol = symbol;
}

public String getSymbol() {
return symbol;
}

public double apply(double x, double y) {
throw new UnsupportedOperationException("Operation not implemented");
}

@Override
public String toString() {
return symbol;
}
}

public double calculate(double x, double y, Operation operation) {
return operation.apply(x, y);
}
}

public class ExecuteOperation {
public static void main(String[] args) {
Calculator calculator = new Calculator();
double result1 = calculator.calculate(5, 3, Calculator.Operation.PLUS);
System.out.println("5 + 3 = " + result1);
}
}

➡ 什麼是nonstatic member class?

nonstatic member class就是沒有static屬性的Nested class,可以存取所有enclosing class的欄位和方法。跟static member class最大的不同在於:

  • 因為可以存取enclosing class的欄位和方法,所以無法自己獨立出去。
  • 每次使用都要實體化成物件。

nonstatic member class常用於Adapter的實作。Adapter的設計方式,可以用於class之間的轉換,以達成資料結構的轉換,像下面的範例,把Collection轉換成Iterator,nonstatic member class可以當作Adapter,協助Collection轉換成像Stack這種資料結構的Iterator。

Press enter or click to view image in full size
import java.util.Iterator;
import java.util.NoSuchElementException;

public class CustomCollection<T> {
private T[] items;
private int size;

@SuppressWarnings("unchecked")
public CustomCollection(int capacity) {
items = (T[]) new Object[capacity];
size = 0;
}

public void add(T item) {
if (size < items.length) {
items[size++] = item;
} else {
throw new IllegalStateException("Collection is full");
}
}

public T get(int index) {
if (index >= 0 && index < size) {
return items[index];
} else {
throw new IndexOutOfBoundsException("Index out of bounds");
}
}

public int size() {
return size;
}

public class CustomIterator implements Iterator<T> {
private int currentIndex = 0;

@Override
public boolean hasNext() {
return currentIndex < size;
}

@Override
public T next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return items[currentIndex++];
}
}

public Iterator<T> iterator() {
return new CustomIterator();
}
}

因為nonstatic member class每次使用都要實體化成物件,比static member class更耗費時間和記憶體,所以設計member class的時候,最好優先設計成static member class,也可以避免memory leak的問題。

➡ 什麼是anonymous class?

Get Judy Huang’s stories in your inbox

Join Medium for free to get updates from this writer.

anonymous class就像anonymous這個字,”匿名”描述的一樣,簡單的說就是沒有名字的類別。這個特殊的類別有幾個特性:

  • 不算是enclosing class中的一個成員,因為短暫使用過後,anonymous class就會消失。
  • 實作完就立刻被實體化成物件。其他類別都是先實作好,等到有需要的時候,在實體化成物件,但anonymous class會同時發生。
  • anonymous class不能是static,也不能宣告有static屬性的欄位。
  • 不能實作多個interface,也不能繼承抽象類別又實作interface。
  • 無法使用instanceof驗證。

總而言之,anonymous class就像一個過客,急急忙忙出現,任務結束後又默默的消失,所以常被用來當function object,舉例來說,collection要做sorting的時候,需要Comparator作為比大小的條件,就可以直接使用anonymous class實作Comparator並實體化物件。

List<Integer> studentIds = new ArrayList<Integer>();

Collections.sort(studentIds, new Comparator<Integer>() {
public int compare(Integer s1, Integer s2) {
return s1.compareTo(s2); // 使用 compareTo 方法進行比較
}
});

也可以用來實作collection架構中的AbstractInterface,像是Item 18的範例一樣,作為兩個資料結構轉換的Adapter。

import java.util.AbstractList;
import java.util.List;
public class ConvertType {
static List<Integer> intArrayAsList(final int[] a) {
if (a == null) {
throw new NullPointerException();
}
return new AbstractList<Integer>() {
@Override
public Integer get(int index) {
return a[index];
}
@Override
public int size() {
return a.length;
}
};
}

public static void main(String[] args) {
int[] myArray = {1, 2, 3};
List<Integer> myList = intArrayAsList(myArray);
System.out.println(myList);
}
}

➡ 什麼是local class?

local class是所有nested class中使用頻率最低的。它跟member class一樣都有名字,跟anonymous class一樣都不能宣告有static屬性的欄位,可以在程式裡面,被匡住的地方宣告和使用,像是方法、if和foor迴圈匡住的地方,就跟下面範例一樣,被宣告在sum()這個範圍的Summation就是local class。

public class Calculator {

public int sum(int[] numbers) {
class Summation {
public int calculateSum() {
int total = 0;
for (int number : numbers) {
total += number;
}
return total;
}
}

Summation summation = new Summation();
return summation.calculateSum();
}

public static void main(String[] args) {
Calculator calculator = new Calculator();
int[] numbers = {1, 2, 3, 4, 5};
int result = calculator.sum(numbers);
System.out.println("Sum: " + result); // 輸出結果:Sum: 15
}
}

Reference:

--

Judy Huang
Judy Huang

Written by Judy Huang

一位專注於AWS、Java、database、devops和刷題的後端工程師,希望幫助大家更好地理解技術