![](/img/trans.png)
[英]Anonymous Inner Type of Standard Java Classes in Content Assist? How are they used?
[英]How are Anonymous inner classes used in Java?
Java中匿名類有什么用? 我們可以說匿名類的使用是Java的優點之一嗎?
通過“匿名類”,我認為您的意思是匿名內部類。
匿名內部類在使用某些“額外”(例如覆蓋方法)創建對象的實例時非常有用,而無需實際對類進行子類化。
我傾向於將其用作附加事件偵聽器的快捷方式:
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// do something
}
});
使用這種方法可以使編碼更快一點,因為我不需要創建一個額外的類來實現ActionListener
—— 我可以只實例化一個匿名內部類,而無需實際創建一個單獨的類。
我只將這種技術用於“快速而骯臟”的任務,在這種情況下,讓整個班級感覺沒有必要。 有多個匿名內部類做完全相同的事情應該重構為一個實際的類,無論是內部類還是單獨的類。
匿名內部類實際上是閉包,因此它們可用於模擬 lambda 表達式或“委托”。 以這個接口為例:
public interface F<A, B> {
B f(A a);
}
您可以匿名使用它在 Java 中創建一流的函數。 假設您有以下方法返回給定列表中大於 i 的第一個數字,如果沒有數字更大,則返回 i :
public static int larger(final List<Integer> ns, final int i) {
for (Integer n : ns)
if (n > i)
return n;
return i;
}
然后你有另一種方法返回給定列表中小於 i 的第一個數字,如果沒有數字更小,則返回 i :
public static int smaller(final List<Integer> ns, final int i) {
for (Integer n : ns)
if (n < i)
return n;
return i;
}
這些方法幾乎相同。 使用一等函數類型 F,我們可以將這些重寫為一個方法,如下所示:
public static <T> T firstMatch(final List<T> ts, final F<T, Boolean> f, T z) {
for (T t : ts)
if (f.f(t))
return t;
return z;
}
您可以使用匿名類來使用 firstMatch 方法:
F<Integer, Boolean> greaterThanTen = new F<Integer, Boolean> {
Boolean f(final Integer n) {
return n > 10;
}
};
int moreThanMyFingersCanCount = firstMatch(xs, greaterThanTen, x);
這是一個非常人為的例子,但很容易看出,能夠將函數當作值來傳遞是一個非常有用的特性。 請參閱喬爾本人的“你的編程語言能做到這一點嗎” 。
以這種風格編寫 Java 的一個不錯的庫: Functional Java。
匿名內部類用於以下場景:
1.) 對於 Overriding(subclassing),當類定義除當前情況外不可用時:
class A{
public void methodA() {
System.out.println("methodA");
}
}
class B{
A a = new A() {
public void methodA() {
System.out.println("anonymous methodA");
}
};
}
2.) 為了實現一個接口,當只需要在當前情況下實現接口時:
interface InterfaceA{
public void methodA();
}
class B{
InterfaceA a = new InterfaceA() {
public void methodA() {
System.out.println("anonymous methodA implementer");
}
};
}
3.) 參數定義的匿名內部類:
interface Foo {
void methodFoo();
}
class B{
void do(Foo f) { }
}
class A{
void methodA() {
B b = new B();
b.do(new Foo() {
public void methodFoo() {
System.out.println("methodFoo");
}
});
}
}
我有時將它們用作 Map 實例化的語法技巧:
Map map = new HashMap() {{
put("key", "value");
}};
對比
Map map = new HashMap();
map.put("key", "value");
它在執行大量 put 語句時節省了一些冗余。 但是,當外部類需要通過遠程處理進行序列化時,我也遇到了問題。
它們通常用作回調的詳細形式。
我想你可以說它們與沒有它們相比是一個優勢,並且每次都必須創建一個命名類,但類似的概念在其他語言中實現得更好(作為閉包或塊)
這是一個揮桿的例子
myButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
// do stuff here...
}
});
雖然它仍然很冗長,但比強迫你為每個丟棄的監聽器定義一個命名類要好得多(盡管取決於情況和重用,這可能仍然是更好的方法)
您可以在需要在另一個函數內為特定目的創建類的情況下使用它,例如,作為偵聽器、作為可運行對象(生成線程)等。
這個想法是你從函數的代碼內部調用它們,所以你永遠不會在其他地方引用它們,所以你不需要命名它們。 編譯器只是枚舉它們。
它們本質上是語法糖,隨着它們變大,通常應該移到其他地方。
我不確定這是否是 Java 的優勢之一,但如果您確實使用它們(不幸的是,我們都經常使用它們),那么您可能會爭辯說它們是其中之一。
匿名類指南。
匿名類同時聲明和初始化。
匿名類必須擴展或實現為一個且僅一個類或接口。
由於匿名類沒有名稱,因此只能使用一次。
例如:
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
}
});
new Thread() {
public void run() {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
System.out.println("Exception message: " + e.getMessage());
System.out.println("Exception cause: " + e.getCause());
}
}
}.start();
這也是使用線程的匿名內部類型的示例之一
是的,匿名內部類絕對是 Java 的優勢之一。
使用匿名內部類,您可以訪問周圍類的最終變量和成員變量,這在偵聽器等中派上用場。
但是一個主要的優點是內部類代碼(至少應該)與周圍的類/方法/塊緊密耦合,具有特定的上下文(周圍的類、方法和塊)。
內部類與外部類的實例相關聯,有兩種特殊類型:本地類和匿名類。 匿名類使我們能夠同時聲明和實例化一個類,從而使代碼簡潔。 當我們只需要一個本地類時,我們會使用它們,因為它們沒有名字。
考慮來自doc的示例,其中我們有一個Person
類:
public class Person {
public enum Sex {
MALE, FEMALE
}
String name;
LocalDate birthday;
Sex gender;
String emailAddress;
public int getAge() {
// ...
}
public void printPerson() {
// ...
}
}
我們有一種方法可以將符合搜索條件的成員打印為:
public static void printPersons(
List<Person> roster, CheckPerson tester) {
for (Person p : roster) {
if (tester.test(p)) {
p.printPerson();
}
}
}
其中CheckPerson
是一個接口,如:
interface CheckPerson {
boolean test(Person p);
}
現在我們可以利用實現此接口的匿名類將搜索條件指定為:
printPersons(
roster,
new CheckPerson() {
public boolean test(Person p) {
return p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25;
}
}
);
這里的接口非常簡單,匿名類的語法顯得笨拙和不清楚。
Java 8引入了一個術語Functional Interface ,它是一個只有一個抽象方法的接口,因此我們可以說CheckPerson
是一個函數接口。 我們可以使用Lambda 表達式,它允許我們將函數作為方法參數傳遞為:
printPersons(
roster,
(Person p) -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
);
我們可以使用標准的函數接口Predicate
代替接口CheckPerson
,這將進一步減少所需的代碼量。
我使用匿名對象來調用新線程..
new Thread(new Runnable() {
public void run() {
// you code
}
}).start();
在為不同的對象提供不同的實現時,匿名內部類可能是有益的。 但是應該非常謹慎地使用,因為它會給程序可讀性帶來問題。
匿名類在類終結中的主要用途之一,稱為終結器監護人。 在 Java 世界中,應該避免使用 finalize 方法,直到您真正需要它們為止。 你必須記住,當你覆蓋子類的 finalize 方法時,你應該總是調用super.finalize()
,因為超類的 finalize 方法不會自動調用,你可能會遇到內存泄漏的問題。
因此,考慮到上述事實,您可以使用匿名類,例如:
public class HeavyClass{
private final Object finalizerGuardian = new Object() {
@Override
protected void finalize() throws Throwable{
//Finalize outer HeavyClass object
}
};
}
使用這種技術,您可以減輕自己和其他開發人員對需要 finalize 方法的HeavyClass
每個子類調用super.finalize()
的HeavyClass
。
你可以這樣使用匿名類
TreeSet treeSetObj = new TreeSet(new Comparator()
{
public int compare(String i1,String i2)
{
return i2.compareTo(i1);
}
});
這里似乎沒有人提到,但您也可以使用匿名類來保存泛型類型參數(通常由於類型擦除而丟失) :
public abstract class TypeHolder<T> {
private final Type type;
public TypeReference() {
// you may do do additional sanity checks here
final Type superClass = getClass().getGenericSuperclass();
this.type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
}
public final Type getType() {
return this.type;
}
}
如果你以匿名方式實例化這個類
TypeHolder<List<String>, Map<Ineger, Long>> holder =
new TypeHolder<List<String>, Map<Ineger, Long>>() {};
那么這樣的holder
實例將包含傳遞類型的非擦除定義。
這對於構建驗證器/反序列化器非常方便。 您還可以使用反射實例化泛型類型(因此,如果您想在參數化類型中執行new T()
- 歡迎使用!) 。
匿名內部類用於創建一個永遠不會被再次引用的對象。 它沒有名稱,在同一個語句中聲明和創建。 這用於通常使用對象變量的地方。 您用new
關鍵字、對構造函數的調用以及{
和}
的類定義替換變量。
用 Java 編寫線程程序時,通常看起來像這樣
ThreadClass task = new ThreadClass();
Thread runner = new Thread(task);
runner.start();
ThreadClass
使用的ThreadClass
將是用戶定義的。 該類將實現創建線程所需的Runnable
接口。 在ThreadClass
, run()
方法(只有Runnable
方法)也需要實現。 很明顯,擺脫ThreadClass
會更有效,這正是匿名內部類存在的原因。
看下面的代碼
Thread runner = new Thread(new Runnable() {
public void run() {
//Thread does it's work here
}
});
runner.start();
此代碼替換了最上面示例中對task
的引用。 Thread()
構造函數中的匿名內部類沒有單獨的類,而是返回一個未命名的對象,該對象實現了Runnable
接口並覆蓋了run()
方法。 方法run()
將包含執行線程所需工作的語句。
在回答匿名內部類是否是 Java 的優勢之一的問題時,我不得不說我不太確定,因為我目前對許多編程語言都不熟悉。 但我可以說的是,它絕對是一種更快、更簡單的編碼方法。
參考資料:Sams 自學 Java 21 天第七版
優化代碼的最佳方式。 此外,我們可以用於類或接口的覆蓋方法。
import java.util.Scanner;
abstract class AnonymousInner {
abstract void sum();
}
class AnonymousInnerMain {
public static void main(String []k){
Scanner sn = new Scanner(System.in);
System.out.println("Enter two vlaues");
int a= Integer.parseInt(sn.nextLine());
int b= Integer.parseInt(sn.nextLine());
AnonymousInner ac = new AnonymousInner(){
void sum(){
int c= a+b;
System.out.println("Sum of two number is: "+c);
}
};
ac.sum();
}
}
還有一個優勢:
正如你所知,Java 不支持多重繼承,所以如果你使用“Thread”類作為匿名類,那么該類仍然有一個空間可供任何其他類擴展。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.