[toc]
https://docs.oracle.com/javase/tutorial/java/IandI/createinterface.html
# interface
## why do we need interface?
有很多時候, 會想要把 標準/實作 分離。
一個範例:
一台印表機,可能都有"列印"的功能。
作為印表機的使用者,你不需要知道底層是如何運作的,只要功能能用就行了。
這是因為所有的印表機都遵守了一個共同的**「介面規範」**,確保所有印表機都具備這些基本功能。
這樣一來,無論你用哪台印表機,都能用相同的方式來操作,而不需要學習不同的控制方法。
同樣, 在Java, 介面就是這樣,確保實作同一個介面的類別能互相溝通。
## in Java
interface(介面) 可以說是一個Method Set template
但是你仍然算是宣告一個參考資料型態
你還是可以到處使用interface Name。
---
## Abstract Method
https://docs.oracle.com/javase/tutorial/java/IandI/abstract.html
是一個無實作的方法(無Method Body, 用`;` 改寫)
必須在abstract class/interface裡面
```java
abstract void print(String s);
```
---
### Members
member 可以有:
- default method(不能被 `static`/`final`/`private`/`protected`修飾)
- 有預設的實作
- 可以被子類別實作
```java
interface IPrinter {
default void print(String s) {
System.out.println(s);
}
}
class TypeA implements IPrinter {
@Override
public void print(String s) {
System.out.println("[MyPrinter]" + s);
}
}
class TypeB implements IPrinter {
// it use the Printer's default print(String s)
}
class Main {
public static void main(String[] args) {
TypeA t = new TypeA();
t.print("hello"); // [MyPrinter] hello
TypeB t2 = new TypeB();
t2.print("world"); // world
}
}
```
- abstract method (不能被 `static`/`default`/`final`/修飾)
- 在Interface中 不能被`private`/`protected`修飾, `default` 會變為 `public`
- 必須被子類別實作
- 在Interface中無須`abstract`修飾
- static method
- `default` 會變為 `public`
- 效果與class的static method一樣。
- private method
- 無法被子類別實作的輔助方法
- variable
- 都會是`public static final`
- 不能是`private` / `protected`
---
範例: variable / static method
這些static的成員不常放在interface裡面。
```java
interface TestInterface {
int i = 1; // 預設會 public static final
private void helperMethod() { // private method
}
static void hello() { // static method
System.out.println("Hello");
}
}
class Main {
public static void main(String[] args) {
int i = TestInterface.i;
TestInterface.hello();
}
}
```
---
範例:
```java=
interface IPrinter {
void print(String s);
}
class TypeA implements IPrinter {
@Override
public void print(String s) {
System.out.println("[A] " + s);
}
}
class TypeB implements IPrinter {
@Override
public void print(String s) {
System.out.println("[B] " + s);
}
}
public class Main {
public static void main(String[] args) {
// 變數型態用Interface
Printer a = new TypeA();
a.print("test"); // [A] test
// 變數型態用自身的class
TypeB b = new TypeB();
b.print("test"); // [B] test
}
}
```
## 變數形態要用介面還是類別?
你說上面的範例, 宣告變數時, 使用Interface還是用Class ?
因為a, b 都是 Printer, 所以可以放進來很合理
```java
public class Main {
public static void main(String[] args) {
Printer a = new TypeA();
TypeB b = new TypeB();
printHelloWorld(a);
printHelloWorld(b);
}
public static void printHelloWorld(Printer c) {
c.print("Hello World");
}
}
```
正因為如此, 如果底層把印表機的邏輯寫好,
把底層的getPrinter寫好,
在比較上層的程式碼, 我們可以不在乎印表機是誰。
透過介面提供的方法操作。
讓程式碼與實作的各種(印表機)類別解耦。
```java
class PrinterManager {
private static Printer typeB = new TypeB();
private static Printer typeA = new TypeA();
public static Printer getPrinter() {
// 這裡可能會找一台沒人在用的Printer之類的
while(...) {
if (...) return typeB;
else if (...) return typeA;
}
}
}
public class Main {
public static void main(String[] args) {
Printer a = PrinterManager.getPrinter();
a.print("hello"); // 用就對了
}
}
```
## interface 繼承 interface
假設interface A extends interface B
如果有個class C implements A
- 等於class C implements A, B
(Interface 能 extend 多個 Interface)
```java
interface Movable {
void move(int x, int y);
}
interface Runnable extends Movable {
void run(int speed);
}
class Human implements {
// move
// run
}
```
class 能 implement 多個interface
```java
interface Movable {
void move(int x, int y);
}
interface Runnable {
void run(int speed);
}
class Human implements Movable, Runnable {
// move
// run
}
```