反射式編程

在電腦學中，反射式編程（reflective programming）或反射（reflection），是指電腦程式在運行時（runtime）可以訪問、檢測和修改它本身狀態或行為的一種能力。用比喻來說，反射就是程序在運行的時候能夠「觀察」並且修改自己的行為。

要注意術語「反射」和「類型內省」（type introspection）的關係。內省（或稱「自省」）機制僅指程序在運行時對自身資訊（稱為元數據）的檢測；反射機制不僅包括要能在運行時對程序自身資訊進行檢測，還要求程序能進一步根據這些資訊改變程序狀態或結構。

## 概況

反射用於觀察並修改程序在運行時的行為。一個反射導向的程式組件可以監測一個範圍內的代碼執行情況，可以根據獲取的目標對象資訊及與此相關的範圍修改自身。這可通過在運行時動態分配程序代碼實現。

在類型檢測嚴格的面向對象的程式語言如Java中，一般需要在編譯期間對程序中需要調用的對象的具體類型、介面（interface）、資料成員（fields）和方法的合法性進行檢查。反射技術則允許將對需要調用的物件的訊息檢查工作從編譯期間推遲到運行期間再現場執行。這樣一來，可以在編譯期間先不明確目標物件的介面（interface）名稱、欄位（fields），即物件的資料成員（成員變量）、可用方法，然後在運行根據目標物件自身的訊息決定如何處理。它還允許根據判斷結果進行實例化新物件和相關方法的呼叫。

反射主要用途就是使給定的程式，動態地適應不同的運行情況。利用物件導向建模中的多型（多態性）也可以簡化編寫分別適用於多種不同情形的功能代碼，但是反射可以解決多型（多態性）並不適用的更普遍情形，從而更大程度地避免硬編碼（即把代碼的細節「寫死」，缺乏靈活性）的代碼風格。

反射也是元編程的一個關鍵策略。

## 歷史背景

早期電腦的原生彙編語言本質上就具有反射特性。因為這些最初架構可以通過定義指令作為數據及使用自修改代碼來編程，實現反射功能是很平常的。編程發展到使用編譯型高級語言如Algol、Cobol、Fortran和包括Pascal和C在內的很多其他語言時，自修改代碼等實踐很大程度上消失了，直到將反射特性內建入類型系統的高級程式語言出現後才再次提供了反射功能。Lisp語言家族以具有同像性作為標誌性特徵，可以認為具有反射性。

1982年，布萊恩·史密斯在其博士論文《程式語言中的過程式反射》中，向過程式程式語言介入了「計算反射」的概念，並且引入自循環直譯器概念用作3-Lisp的一個組成部份。

## 特點

### 優點

支持反射的語言提供了一些在早期高級語言中難以實現的運行時特性。

- 可以在一定程度上避免硬編碼，提供靈活性和通用性。
- 可以作為一個頭等物件發現並修改原始碼的結構（如代碼塊、類、方法、協議等）。
- 可以在運行時像對待原始碼語句一樣動態解析字符串中可執行的代碼（類似JavaScript的eval函數），進而可將跟class或function匹配的字串轉換成class或function的調用或引用。
- 可以創建一個新的語言字節碼解釋器來給編程結構一個新的意義或用途。

### 劣勢

- 此技術的學習成本高。面向反射的編程需要較多的高級知識，包括框架、關係映射和對象交互，以實現更通用的代碼執行。
- 同樣因為反射的概念和語法都比較抽象，過多地濫用反射技術會使得代碼難以被其他人讀懂，不利於合作與交流。
- 由於將部分資訊檢查工作從編譯期推遲到了運行期，此舉在提高了代碼靈活性的同時，犧牲了一點點運行效率。

通過深入學習反射的特性和技巧，它的劣勢可以儘量避免，但這需要許多時間和經驗的積累。

## 例子

下列代碼片段建立類Foo的一個實例foo，並調用它的方法PrintHello。對於每個程式語言，展示平常的和基於反射的調用序列。

### C#

```c#
// Without reflection
Foo foo = new Foo();
foo.PrintHello();

// With reflection
System.Object foo2 = System.Activator.CreateInstance(System.Type.GetType("complete.classpath.and.Foo"));
System.Reflection.MethodInfo method = foo2.GetType().GetMethod("PrintHello");
method.Invoke(foo2, null);
```

### Go

```go
import "reflect"

// Without reflection
f := Foo{}
f.Hello()

// With reflection
fT := reflect.TypeOf(Foo{})
fV := reflect.New(fT)

m := fV.MethodByName("Hello")
if m.IsValid() {
    m.Call(nil)
}
```

### Java

```java
import java.lang.reflect.Method;

// Without reflection
Foo foo = new Foo();
foo.hello();

// With reflection
try {
    // Alternatively: Object foo = Foo.class.newInstance();
    Object foo = Class.forName("complete.classpath.and.Foo").newInstance();

    Method m = foo.getClass().getDeclaredMethod("hello", new Class<?>[0]);
    m.invoke(foo);
} catch (Exception e) {
    // Catching ClassNotFoundException, NoSuchMethodException
    // InstantiationException, IllegalAccessException
}
```

### Perl

```perl
# Without reflection
my $foo = Foo->new;
$foo->hello;

# or
Foo->new->hello;

# With reflection
my $class = "Foo"
my $constructor = "new";
my $method = "hello";

my $f = $class->$constructor;
$f->$method;

# or
$class->$constructor->$method;

# with eval
eval "new Foo->hello;";
```

### PHP

```php
// Without reflection
$foo = new Foo();
$foo->hello();

// With reflection, using Reflections API
$reflector = new ReflectionClass('Foo');
$foo = $reflector->newInstance();
$hello = $reflector->getMethod('hello');
$hello->invoke($foo);
```

### Python

```python
# Without reflection
obj = Foo()
obj.hello()

# With reflection
obj = globals()['Foo']() # globals() Return a dictionary representing the current global symbol table. 
getattr(obj, 'hello')()  # getattr(object, name) Return the value of the named attribute of object.

# With eval
eval('Foo().hello()')
```

### R

```RSPlus
# Without reflection, assuming foo() returns an S3-type object that has method "hello"
obj <- foo()
hello(obj)

# With reflection
the.class <- "foo"
the.method <- "hello"
obj <- do.call(the.class, list())
do.call(the.method, alist(obj))
```

### Ruby

```ruby
# Without reflection
obj = Foo.new
obj.hello

# With reflection
class_name = "Foo"
method_name = :hello
obj = Object.const_get(class_name).new
obj.send method_name

# With eval
eval "Foo.new.hello"
```

## 常見應用

- 反射經常作為軟體測試的一部分，比如運行時創建/實例化模擬對象。
- Java語言解析XML文件的技術用到了反射。

## 外部連結

- Reflection in logic, functional and object-oriented programming: a short comparative study
- An Introduction to Reflection-Oriented Programming
- Brian Foote's pages on Reflection in Smalltalk
- Java Reflection API Tutorial from Oracle