Яке використання анонімних класів на Java? Чи можна сказати, що використання анонімного класу є однією з переваг Java?
Яке використання анонімних класів на Java? Чи можна сказати, що використання анонімного класу є однією з переваг Java?
Відповіді:
Під "анонімним класом" я вважаю, що ви маєте на увазі анонімний внутрішній клас .
Анонімний внутрішній клас може стати корисним при створенні екземпляра об'єкта з певними "додатками", такими як переосмислювальні методи, без фактичного підкласу класу.
Я схильний використовувати його як ярлик для приєднання слухача подій:
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// do something
}
});
Використання цього методу робить кодування трохи швидшим, оскільки мені не потрібно робити додатковий клас, який реалізується ActionListener
- я можу просто інстанціювати анонімний внутрішній клас, фактично не складаючи окремого класу.
Я використовую цю техніку лише для "швидких і брудних" завдань, коли цілий клас відчуває себе непотрібним. Маючи кілька анонімних внутрішніх класів, які роблять точно те саме, слід переробити фактичний клас, будь то внутрішній клас або окремий клас.
overloading methods
а ні overriding methods
?
Анонімні внутрішні класи - це фактично закриття, тому їх можна використовувати для імітації лямбда-виразів або "делегатів". Наприклад, візьміть цей інтерфейс:
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 в такому стилі: Функціональна Java.
Анонімний внутрішній клас використовується в наступному сценарії:
1.) Для переосмислення (підкласифікації), коли визначення класу не використовується, крім поточного випадку:
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");
Це економить зайву кількість зайвих заяв. Однак я також зіткнувся з проблемами цього, коли зовнішній клас потрібно серіалізувати за допомогою видалення.
Вони зазвичай використовуються як багатослівна форма зворотного виклику.
Я гадаю, ви можете сказати, що вони є перевагою порівняно з тим, що їх немає, і щоразу потрібно створювати названий клас, але подібні концепції реалізуються набагато краще в інших мовах (як закриття чи блоки)
Ось гойдаючий приклад
myButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
// do stuff here...
}
});
Хоча це все ще безладно багатослівно, це набагато краще, ніж змушувати вас визначати названий клас для кожного слухача, який викидає, як це (хоча залежно від ситуації та повторного використання, це може бути кращим підходом)
myButton.addActionListener(e -> { /* do stuff here */})
або myButton.addActionListener(stuff)
було б коротше.
Ви використовуєте його в ситуаціях, коли вам потрібно створити клас з певною метою всередині іншої функції, наприклад, як слухач, як виконаний (для нерестування потоку) тощо.
Ідея полягає в тому, що ви викликаєте їх всередині коду функції, щоб ви ніколи не посилалися на них в іншому місці, тому вам не потрібно їх називати. Компілятор просто їх перераховує.
Вони по суті є синтаксичним цукром, і зазвичай їх слід переміщувати в інше місце, коли вони зростають.
Я не впевнений, що це одна з переваг Java, хоча якщо ви їх використовуєте (і ми, на жаль, ми часто їх використовуємо), то ви можете стверджувати, що вони є одним.
GuideLines для анонімного класу.
Анонімний клас оголошується та ініціалізується одночасно.
Анонімний клас повинен поширюватися або впроваджуватися до одного і лише одного класу або інтерфейсу, відповідно.
Оскільки клас анонімних мишей не має імені, його можна використовувати лише один раз.
наприклад:
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
}
});
ref.getClass().newInstance()
.
Так, анонімні внутрішні класи, безумовно, є однією з переваг Java.
З анонімним внутрішнім класом у вас є доступ до остаточних і членів змінних навколишнього класу, що стане в нагоді слухачам і т.д.
Але головна перевага полягає в тому, що код внутрішнього класу, який (принаймні повинен бути) щільно поєднаний з навколишнім класом / методом / блоком, має специфічний контекст (оточуючий клас, метод та блок).
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();
Це також один із прикладів для анонімного внутрішнього типу з використанням потоку
я використовую анонімні об'єкти для виклику нових тем ..
new Thread(new Runnable() {
public void run() {
// you code
}
}).start();
Внутрішній клас пов'язаний з екземпляром зовнішнього класу і є два особливих види: Місцевий клас і анонімний клас . Анонімний клас дозволяє нам оголошувати та інстанціювати клас одночасно, тому робить код стислим. Ми використовуємо їх, коли нам потрібен місцевий клас лише один раз, оскільки вони не мають імені.
Розглянемо приклад з 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 представила термін функціональний інтерфейс, який є інтерфейсом лише з одним абстрактним методом, отже, можна сказати CheckPerson
, це функціональний інтерфейс. Ми можемо використовувати Lambda Expression, який дозволяє нам передавати функцію як аргумент методу як:
printPersons(
roster,
(Person p) -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
);
Ми можемо використовувати стандартний функціональний інтерфейс Predicate
замість інтерфейсу CheckPerson
, що ще більше зменшить кількість необхідного коду.
Анонімний внутрішній клас може бути корисним при наданні різних реалізацій для різних об'єктів. Але його слід використовувати дуже щадно, оскільки це створює проблеми для читабельності програми.
Одне з головних застосувань анонімних класів при доопрацюванні класу, яке називається опікуном фіналізатора . У світі Java використання методів доопрацювання слід уникати, поки вони вам справді не потрібні. Ви повинні пам’ятати, що коли ви переосмислюєте метод доопрацювання для підкласів, завжди слід також посилатись super.finalize()
, тому що метод фіналізації суперкласу не викликатиметься автоматично, і у вас можуть виникнути проблеми з витоками пам’яті.
тож з огляду на зазначений вище факт, ви можете просто використовувати анонімні класи типу:
public class HeavyClass{
private final Object finalizerGuardian = new Object() {
@Override
protected void finalize() throws Throwable{
//Finalize outer HeavyClass object
}
};
}
Використовуючи цю техніку, ви позбавили себе та інших розробників звертатися super.finalize()
до кожного підкласу, HeavyClass
який потребує методу доопрацювання.
Здається, тут ніхто не згадується, але ви також можете використовувати анонімний клас для проведення аргументу загального типу (який зазвичай втрачається через стирання типу) :
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()
параметризований тип - вас вітає!) .
Найкращий спосіб оптимізації коду. Також ми можемо використовувати для переважаючого класу або інтерфейсу.
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();
}
}
Анонімний внутрішній клас використовується для створення об'єкта , який ніколи не буде посилатися знову. Він не має імені і оголошується та створюється в одному і тому ж висловлюванні. Він використовується там, де ви зазвичай використовуєте змінну об'єкта. Ви замінюєте змінну new
ключовим словом, викликом конструктора та визначенням класу всередині {
та }
.
Коли ви пишете програму Threaded на Java, це зазвичай виглядає так
ThreadClass task = new ThreadClass();
Thread runner = new Thread(task);
runner.start();
ThreadClass
Використовується тут буде визначена користувачем. Цей клас реалізує Runnable
інтерфейс, необхідний для створення потоків. У метод (єдиний метод в ) повинна бути реалізована , а також. Зрозуміло, що позбутися було б ефективніше, і саме тому існують анонімні внутрішні класи.ThreadClass
run()
Runnable
ThreadClass
Подивіться на наступний код
Thread runner = new Thread(new Runnable() {
public void run() {
//Thread does it's work here
}
});
runner.start();
Цей код замінює посилання, наведене task
у верхньому прикладі. Замість того, щоб мати окремий клас, Anonymous Inner Class всередині Thread()
конструктора повертає неназваний об'єкт, який реалізує Runnable
інтерфейс і замінює run()
метод. Метод run()
буде включати оператори всередині, які виконують роботу, необхідну для потоку.
Відповідаючи на питання про те, чи є Anonymous Inner Classes однією з переваг Java, я повинен би сказати, що я не зовсім впевнений, оскільки на сьогоднішній день я не знайомий з багатьма мовами програмування. Але те, що я можу сказати, це, безумовно, швидший і простіший метод кодування.
Список літератури: Самс навчить себе Java у сьомому виданні за 21 день