Java класс без экземпляра

Анонимные внутренние классы

В языке Java есть возможность создавать анонимные классы, то есть, классы без имени. На этом занятии мы увидим как они объявляются и для чего используются.

Итак, анонимные внутренние классы всегда используются как расширения обычных классов или интерфейсов. То есть, изначально нам нужно объявить какой-нибудь обычный класс (или интерфейс), например:

а, затем, в процессе создания экземпляра этого класса, мы можем расширить его функционал через анонимный внутренний класс. Это делается по следующему синтаксису:

Смотрите, вот эти фигурные скобки, что идут после создания экземпляра класса (или интерфейса) и есть объявление внутреннего анонимного класса, так как у него мы явно не указываем никакого имени. Хотя имя у него все-таки есть. Виртуальная машина Java автоматически присвоит ему имя по правилу

где X – целое число (порядковый номер анонимного класса).

То есть, в нашем примере с классом Button, мы можем объявить вложенный анонимный класс, следующим образом:

Правда функционала здесь пока еще никакого нет. Давайте его добавим, а заодно увидим для чего все это нужно. Предположим, что при нажатии на кнопку должен вызываться метод click() и этот метод мы пропишем внутри класса Button:

class Button { public void click() { System.out.println("Нажатие на кнопку"); } }

Но далее, по программе собираемся использовать этот класс кнопки для разных задач: открытия файла, сохранения проекта, копирования данных и т.п. Используя наши текущие знания, мы, конечно же создали бы несколько дочерних классов и переопределили в них метод click():

class ButtonOpen extends Button { public void click() { System.out.println("Открытие файла"); } }

И так для каждой операции. В результате, у нас в программе появится большое число разных дочерних классов и их количество будет постоянно расти по мере развития проекта. Это не очень удобно. Вот здесь, как раз, нам на помощь и приходят внутренние анонимные классы. Вместо создания дочерних классов, мы при создании объекта Button, сразу переопределим его метод click с помощью анонимного класса:

Button btnCopy = new Button() { public void click() { System.out.println("Копирование данных"); } };

И, затем, вызывая этот метод:

в консоли увидим сообщение:

То есть, с помощью внутреннего анонимного класса мы «на лету» переопределили его метод click() и задали нужное нам действие. Никаких дочерних классов создавать не потребовалось. Мало того, текст программы стал более прозрачным и ясным. Программист непосредственно в момент создания объекта видит, что именно он будет выполнять при нажатии на кнопку. Все это и обусловливает удобство вложенных анонимных классов.

Давайте теперь внимательнее посмотрим, как работает эта конструкция. Проведем такой маленький эксперимент. Если в анонимный класс добавить некий метод:

Button btnCopy = new Button() { public void click() { System.out.println("Копирование данных"); } public void doSome() { System.out.println("do something. "); } };

А, затем, вызвать его через ссылку btnCopy:

то возникнет ошибка. Спрашивается: почему мы можем обращаться к методу click(), но не можем вызывать метод doSome()? Я думаю, вы уже догадались почему. Все дело в том, что анонимный класс фактически наследуется от базового класса Button. В этом смысл слова «внутренний» анонимный класс. Поэтому, мы можем вызывать метод click() благодаря механизму динамической диспетчеризации методов, так как такой же метод click() объявлен в классе Button. А вот «добраться» до метода doSome() уже нет никакой возможности. Но, если нам изменить тип ссылки btnCopy так, чтобы она имела тип анонимного вложенного класса:

то проблем с вызовом метода doSome() не возникнет, т.к. ссылка btnCopy теперь имеет тип дочернего анонимного класса (благодаря ключевому слову var, виртуальная машина Java в момент выполнения кода автоматически приводит ссылку к нужному типу данных).

Этот пример еще раз показывает, что здесь создается не объект класса Button, а объект дочернего анонимного класса.

Использование анонимных классов с интерфейсами

В нашей реализации с кнопкой есть один существенный недостаток: анонимный класс встраивается в цепочку наследования:

В результате, программист может случайно (или намеренно) переопределить методы и нарушить штатную работу объекта Button. Большую гибкость и безопасность работы можно достичь, используя интерфейсы. Например, можно определить интерфейс EventHandler, связанный с обработкой определенного типа события, в котором объявлен абстрактный метод execute(). Далее, мы можем при создании экземпляра класса Button создать класс, реализующий интерфейс EventHandler с помощью анонимного класса, в котором определим нужную нам реализацию метода execute:

На уровне языка Java все это можно реализовать так. Сначала запишем интерфейс EventHandler и класс Button:

interface EventHandler { void execute(); } class Button { EventHandler handler; Button(EventHandler handler) { this.handler = handler; } public void click() { handler.execute(); } }

А, затем, создадим кнопку в методе main():

public class Main { public static void main(String[] args) { var btnCopy = new Button(new EventHandler() { public void execute() { System.out.println("Копирование данных"); } }); btnCopy.click(); } }

Смотрите, как элегантно и красиво выглядит реализация обработки события в классе Button. Сначала, при создании экземпляра кнопки мы тут же создаем объект анонимного вложенного класса, реализующий интерфейс EventHandler. Поэтому, внутри фигурных скобок обязаны определить метод execute() с конкретной реализацией. Далее, ссылку на созданный объект анонимного класса передаем как аргумент в конструктор класса Button и сохраняем ее, используя тип интерфейса EventHandler. Нам этого вполне достаточно, так как затем, в методе click() вызываем переопределенный метод execute() в анонимном классе через ссылку handler. Все, таким образом, мы отделили обработку события от реализации класса Button и, кроме того, можем разными интерфейсами описывать разные типы событий. Это обеспечивает дополнительную гибкость программного кода.

Вот так анонимные вложенные классы позволяют «на лету» создавать нужные нам объекты на основе других классов или интерфейсов.

Видео по теме

#11 Концепция объектно-ориентированного программирования (ООП)

#12 Классы и создание объектов классов

#13 Конструкторы, ключевое слово this, инициализаторы

#14 Методы класса, сеттеры и геттеры, public, private, protected

#15 Пакеты, модификаторы конструкторов и классов

#16 Ключевые слова static и final

#17 Внутренние и вложенные классы

#18 Как делается наследование классов

#19 Ключевое слово super, оператор instanceof

#20 Модификаторы private и protected, переопределение методов, полиморфизм

#21 Абстрактные классы и методы

#22 Интерфейсы — объявление и применение

#23 Интерфейсы — приватные, статические и дефолтные методы, наследование интерфейсов

#24 Анонимные внутренние классы

#26 Обобщения классов (Generics)

#27 Ограничения типов, метасимвольные аргументы, обобщенные методы и конструкторы

#28 Обобщенные интерфейсы, наследование обобщенных классов

© 2023 Частичное или полное копирование информации с данного сайта для распространения на других ресурсах, в том числе и бумажных, строго запрещено. Все тексты и изображения являются собственностью сайта

Источник

Java класс без экземпляра

У меня в идее все декларирует(компиляторо не ругается) я декларировал и переменные статические и создавал статические методы, другой вопрос что статику из анонимного класса не достать. Почему автор так написал? Может я что то не так понял? Есть ответы: Внутренний анонимный класс может содержать статические переменные и методы c версии Java 16! Статические методы или переменные достать можно, просто нужно обратиться к ним в main методе внутри воложеного класса: System.out.println(staticInt); Иначе как сделать извлечение статик элементов из анонимного класса я не придумал.

Предыдущая статья про нестатические вложенные классы (1/3 часть): https://javarush.com/groups/posts/2181-vlozhennihe-vnutrennie-klassih Продолжение про статические вложенные классы (3/3 часть): https://javarush.com/groups/posts/2183-staticheskie-vlozhennihe-klassih

вот кое что не понятно. Тут написано: «Если каждому из наших анонимных классов-модулей понадобится какое-то отличающееся поведение, свои специфические методы, которых нет у других, мы легко можем дописать их:»

 MonitoringSystem generalModule = new MonitoringSystem() < @Override public void startMonitoring() < System.out.println("Мониторинг общих показателей стартовал!"); >public void someSpecificMethod() < System.out.println("Специфический метод только для первого модуля"); >>; 

Вопрос. А имеет смысл такие методы вообще писать (даже если пропустит компилятор)? будет ли вообще хоть какая-нибудь возможность достучаться до специфического метода? Ведь данный метод существует лишь в анонимном классе, имя которого нам не известно. Объект анонимного класса хранится в переменной типа интерфейса. Данному интерфейсу ничего не известно о данном методе (который есть только у наследника — в анонимном классе). И компилятор по этой причине не позволит к нему обратиться. Более того, мы не сможем создать ссылочную переменную типа анонимного класса и перекинуть туда объект, чтобы вызвать данный метод — по причине того, что нам банально не известно имя анонимного класса, чтобы создать ссылочную переменную данного типа. Я прав?

Супер лекция. Еще бы компоновали ссылки в конце по смежным темам(здесь например по всем типам вложенных классов). А то искать через поисковик неудобно. Или я чего-то не догоняю.

 import java.util.Scanner; interface Eatable < public void eat(); >public class Test < public static final Scanner menu = new Scanner(System.in); public static final String whatWouldULikeToEat = menu.nextLine(); public static void main(String[] args) < Eatable eatable = new Eatable() < @Override public void eat() < System.out.printf("Well, I would like to eat %s, please.", whatWouldULikeToEat); >public String receipt() < return whatWouldULikeToEat; >>; eatable.eat(); System.out.println("\nOkay, sir."); > > 

Важный момент в том, что дополнительные методы, помимо тех, что мы переопределяем, нельзя использовать извне этого анонимного класса, а только внутри него. Т.е. в примере:

 MonitoringSystem generalModule = new MonitoringSystem() < @Override public void startMonitoring() < System.out.println("Мониторинг общих показателей стартовал!"); >public void someSpecificMethod() < System.out.println("Специфический метод только для первого модуля"); >>; 

Источник

Читайте также:  Обработка csv данных python
Оцените статью