Do czego służą anonimowe klasy w Javie? Czy możemy powiedzieć, że korzystanie z anonimowej klasy jest jedną z zalet Javy?
Do czego służą anonimowe klasy w Javie? Czy możemy powiedzieć, że korzystanie z anonimowej klasy jest jedną z zalet Javy?
Odpowiedzi:
Przez „anonimową klasę” rozumiem, że masz na myśli anonimową klasę wewnętrzną .
Anonimowa klasa wewnętrzna może się przydać, gdy tworzy się instancję obiektu z pewnymi „dodatkami”, takimi jak metody przesłonięcia, bez konieczności faktycznej podklasy klasy.
Zwykle używam go jako skrótu do dołączania detektora zdarzeń:
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// do something
}
});
Zastosowanie tej metody sprawia, że kodowanie jest trochę szybsze, ponieważ nie muszę tworzyć dodatkowej klasy, która się implementuje ActionListener
- mogę po prostu utworzyć anonimową klasę wewnętrzną bez tworzenia osobnej klasy.
Używam tej techniki tylko do zadań „szybkich i brudnych”, w których tworzenie całej klasy wydaje się niepotrzebne. Posiadanie wielu anonimowych klas wewnętrznych, które robią dokładnie to samo, należy przekształcić w klasę rzeczywistą, czy to klasę wewnętrzną, czy osobną.
overloading methods
a nie overriding methods
?
Anonimowe klasy wewnętrzne są skutecznie zamknięciami, więc można ich używać do emulacji wyrażeń lambda lub „delegatów”. Na przykład weź ten interfejs:
public interface F<A, B> {
B f(A a);
}
Możesz użyć tego anonimowo, aby stworzyć pierwszorzędną funkcję w Javie. Załóżmy, że masz następującą metodę, która zwraca pierwszą liczbę większą niż i na podanej liście lub i, jeśli żadna liczba nie jest większa:
public static int larger(final List<Integer> ns, final int i) {
for (Integer n : ns)
if (n > i)
return n;
return i;
}
A następnie masz inną metodę, która zwraca pierwszą liczbę mniejszą niż i na podanej liście lub i, jeśli żadna liczba nie jest mniejsza:
public static int smaller(final List<Integer> ns, final int i) {
for (Integer n : ns)
if (n < i)
return n;
return i;
}
Te metody są prawie identyczne. Używając funkcji pierwszej klasy typu F, możemy przepisać je na jedną z metod w następujący sposób:
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;
}
Możesz użyć anonimowej klasy, aby użyć metody firstMatch:
F<Integer, Boolean> greaterThanTen = new F<Integer, Boolean> {
Boolean f(final Integer n) {
return n > 10;
}
};
int moreThanMyFingersCanCount = firstMatch(xs, greaterThanTen, x);
To naprawdę wymyślony przykład, ale łatwo zauważyć, że możliwość przekazywania funkcji tak, jakby były wartościami, jest dość przydatną funkcją. Zobacz „Czy Twój język programowania może to zrobić” autorstwa samego Joela.
Ładna biblioteka do programowania Java w tym stylu: funkcjonalna Java.
Anonimowa klasa wewnętrzna jest używana w następującym scenariuszu:
1.) W przypadku przesłonięcia (podklasy), gdy definicja klasy nie jest użyteczna z wyjątkiem bieżącego przypadku:
class A{
public void methodA() {
System.out.println("methodA");
}
}
class B{
A a = new A() {
public void methodA() {
System.out.println("anonymous methodA");
}
};
}
2.) W przypadku implementacji interfejsu, gdy implementacja interfejsu jest wymagana tylko w bieżącym przypadku:
interface interfaceA{
public void methodA();
}
class B{
interfaceA a = new interfaceA() {
public void methodA() {
System.out.println("anonymous methodA implementer");
}
};
}
3.) Anonimowa klasa wewnętrzna zdefiniowana w argumencie:
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");
}
});
}
}
Czasami używam ich jako hacków składniowych do tworzenia instancji Map:
Map map = new HashMap() {{
put("key", "value");
}};
vs
Map map = new HashMap();
map.put("key", "value");
Oszczędza to nadmiarowości przy wykonywaniu wielu instrukcji put. Jednak napotkałem również problemy podczas robienia tego, gdy klasa zewnętrzna musi być serializowana poprzez zdalne sterowanie.
Są one powszechnie stosowane jako pełna forma oddzwaniania.
Przypuszczam, że można powiedzieć, że są zaletą w porównaniu z ich brakiem i koniecznością tworzenia nazwanej klasy za każdym razem, ale podobne koncepcje są wdrażane znacznie lepiej w innych językach (jako zamknięcia lub bloki)
Oto przykład huśtawki
myButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
// do stuff here...
}
});
Mimo że wciąż jest bałaganiarski, jest o wiele lepszy niż zmuszanie cię do zdefiniowania nazwanej klasy dla każdego takiego wyrzucającego słuchacza (chociaż w zależności od sytuacji i ponownego użycia może to być lepsze podejście)
myButton.addActionListener(e -> { /* do stuff here */})
lub myButton.addActionListener(stuff)
byłoby terser.
Używasz go w sytuacjach, w których musisz utworzyć klasę do określonego celu w innej funkcji, np. Jako słuchacz, jako uruchamialny (w celu odrodzenia wątku) itp.
Chodzi o to, że wywołujesz je z wnętrza kodu funkcji, abyś nigdy nie odwoływał się do nich gdzie indziej, więc nie musisz ich nazywać. Kompilator po prostu je wylicza.
Zasadniczo są one cukrem syntaktycznym i powinny być przenoszone gdzie indziej, gdy rosną.
Nie jestem pewien, czy jest to jedna z zalet Javy, ale jeśli z nich korzystasz (i wszyscy często je wykorzystujemy, niestety), możesz argumentować, że są jedną z nich.
GuideLines dla anonimowej klasy.
Anonimowa klasa jest deklarowana i inicjowana jednocześnie.
Anonimowa klasa musi rozszerzyć lub zaimplementować jedną lub tylko jedną klasę lub interfejs.
Ponieważ klasa anonimowa nie ma nazwy, można jej użyć tylko raz.
na przykład:
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
}
});
ref.getClass().newInstance()
.
Tak, anonimowe klasy wewnętrzne są zdecydowanie jedną z zalet Javy.
Dzięki anonimowej klasie wewnętrznej masz dostęp do zmiennych końcowych i zmiennych klasy otaczającej, co przydaje się w słuchaczach itp.
Ale główną zaletą jest to, że wewnętrzny kod klasy, który jest (przynajmniej powinien być) ściśle powiązany z otaczającą klasą / metodą / blokiem, ma określony kontekst (otaczająca klasa, metoda i blok).
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();
Jest to również jeden z przykładów anonimowego typu wewnętrznego używającego wątku
używam anonimowych obiektów do wywoływania nowych wątków ..
new Thread(new Runnable() {
public void run() {
// you code
}
}).start();
Klasa wewnętrzna jest związana z wystąpieniem zewnętrznej klasy i są dwa specjalne rodzaje: klasa lokalna i klasa Anonymous . Anonimowa klasa umożliwia nam deklarowanie i tworzenie instancji klasy w tym samym czasie, dzięki czemu kod jest zwięzły. Używamy ich, gdy potrzebujemy lokalnej klasy tylko raz, ponieważ nie mają nazwy.
Rozważ przykład z dokumentu, w którym mamy Person
klasę:
public class Person {
public enum Sex {
MALE, FEMALE
}
String name;
LocalDate birthday;
Sex gender;
String emailAddress;
public int getAge() {
// ...
}
public void printPerson() {
// ...
}
}
i mamy metodę drukowania członków spełniających kryteria wyszukiwania jako:
public static void printPersons(
List<Person> roster, CheckPerson tester) {
for (Person p : roster) {
if (tester.test(p)) {
p.printPerson();
}
}
}
gdzie CheckPerson
jest interfejs taki jak:
interface CheckPerson {
boolean test(Person p);
}
Teraz możemy skorzystać z anonimowej klasy, która implementuje ten interfejs do określenia kryteriów wyszukiwania jako:
printPersons(
roster,
new CheckPerson() {
public boolean test(Person p) {
return p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25;
}
}
);
Tutaj interfejs jest bardzo prosty, a składnia anonimowej klasy wydaje się niewygodna i niejasna.
Java 8 wprowadziła termin Interfejs funkcjonalny, który jest interfejsem z tylko jedną metodą abstrakcyjną, dlatego możemy powiedzieć, że CheckPerson
jest interfejsem funkcjonalnym. Możemy skorzystać z wyrażenia Lambda, które pozwala nam przekazać funkcję jako argument metody jako:
printPersons(
roster,
(Person p) -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
);
Predicate
Zamiast interfejsu możemy użyć standardowego interfejsu funkcjonalnego CheckPerson
, co dodatkowo zmniejszy ilość wymaganego kodu.
Anonimowa klasa wewnętrzna może być korzystna, dając różne implementacje dla różnych obiektów. Ale należy go używać bardzo oszczędnie, ponieważ stwarza to problemy z czytelnością programu.
Jednym z głównych zastosowań anonimowych klas w procesie finalizacji klas, który nazywał opiekuna finalizatora . W świecie Java należy unikać stosowania metod finalizacji, dopóki nie będą one naprawdę potrzebne. Musisz pamiętać, że kiedy przesłonisz metodę finalizacji dla podklas, zawsze powinieneś wywoływaćsuper.finalize()
, ponieważ metoda finalizacji superklasy nie wywoła się automatycznie i możesz mieć problemy z wyciekami pamięci.
uwzględniając powyższy fakt, możesz po prostu użyć anonimowych klas, takich jak:
public class HeavyClass{
private final Object finalizerGuardian = new Object() {
@Override
protected void finalize() throws Throwable{
//Finalize outer HeavyClass object
}
};
}
Korzystając z tej techniki, odciążyłeś siebie i innych programistów od wywołania super.finalize()
każdej podklasy, HeavyClass
która wymaga metody finalizacji.
Wygląda na to, że nikt tu nie był wymieniony, ale możesz także użyć klasy anonimowej do przechowywania ogólnego argumentu typu (który zwykle traci się z powodu usunięcia typu) :
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;
}
}
Jeśli utworzysz tę klasę w sposób anonimowy
TypeHolder<List<String>, Map<Ineger, Long>> holder =
new TypeHolder<List<String>, Map<Ineger, Long>>() {};
to takie holder
instancja będzie zawierać nieusuniętą definicję przekazywanego typu.
Jest to bardzo przydatne do budowania walidatorów / deserializatorów. Możesz także utworzyć typ ogólny z odbiciem (więc jeśli kiedykolwiek chciałeś zrobić new T()
w parametryzowanym typie - nie ma za co!) .
Najlepszy sposób na optymalizację kodu. możemy również użyć do nadrzędnej metody klasy lub interfejsu.
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();
}
}
Anonymous Klasa wewnętrzna służy do tworzenia obiektu, który nigdy nie zostanie ponownie odwołuje. Nie ma nazwy, jest zadeklarowany i utworzony w tej samej instrukcji. Jest to używane tam, gdzie normalnie użyłbyś zmiennej obiektu. Zmienną zamieniasz new
słowo kluczowe, wywołanie konstruktora i definicję klasy wewnątrz {
i }
.
Podczas pisania programu wątkowego w Javie zwykle wyglądałby tak
ThreadClass task = new ThreadClass();
Thread runner = new Thread(task);
runner.start();
ThreadClass
Stosowane tutaj byłoby zdefiniowane przez użytkownika. Ta klasa zaimplementuje Runnable
interfejs wymagany do tworzenia wątków. W (metoda tylko w ) musi zostać wdrożony, jak również. Oczywiste jest, że się go pozbyćThreadClass
run()
Runnable
ThreadClass
byłoby bardziej wydajne i właśnie dlatego istnieją Anonimowe Klasy Wewnętrzne.
Spójrz na następujący kod
Thread runner = new Thread(new Runnable() {
public void run() {
//Thread does it's work here
}
});
runner.start();
Ten kod zastępuje odniesienie do task
najbardziej znanego przykładu. Zamiast mieć osobną klasę, Anonimowa Klasa Wewnętrzna wewnątrz Thread()
konstruktora zwraca nienazwany obiekt, który implementuje Runnable
interfejs i zastępuje run()
metodę. Metoda run()
obejmowałaby wewnątrz instrukcje, które wykonują pracę wymaganą przez wątek.
Odpowiadając na pytanie, czy Anonimowe Klasy Wewnętrzne są jedną z zalet Javy, muszę powiedzieć, że nie jestem do końca pewien, ponieważ nie znam obecnie wielu języków programowania. Ale mogę powiedzieć, że jest to zdecydowanie szybsza i łatwiejsza metoda kodowania.
Referencje: Sams Teach Your Jav Java in 21 Days Seventh Edition