Введение в ООП в Python
Python — это мультипарадигмальный язык. Это означает, что он поддерживает различные подходы к программированию.
Одной из наиболее популярных парадигм является создание объектов. Она известна как объектно-ориентированное программирование (ООП).
Объект имеет две характеристики:
- атрибуты;
- поведение;
Рассмотрим на примере:
Объект – это попугай:
- имя, возраст, цвет являются атрибутами;
- пение, танцы — это поведение;
Концепция ООП в Python направлена на создание кода для многократного использования. Эта концепция также известна как DRY (Don’t Repeat Yourself).
В Python концепция ООП реализует несколько принципов:
Наследование | Использование элементов из нового класса без изменения существующего класса. |
Инкапсуляция | Скрытие приватных элементов класса от других объектов. |
Полиморфизм | Концепция использования объекта с одинаковым интерфейсом без получения информации о его типе и внутренней структуре. |
Определяем декораторы с помощью классов
В этом разделе мы рассмотрим как использовать классы для создания декораторов. И так, классы могут быть очень полезны, так как позволяют избежать вложенной архитектуры при определении декораторов. Кроме того полезно использовать классы для написания декораторов с сохранением некоторого внутреннего состояния от вызова к вызову. Ниже приведен пример кода, который поясняет как можно реализовать декоратор с использованием классов.
class ClassDeco: def __init__(self, func): functools.update_wrapper(self, func) self.func = func def __call__(self, *args, **kwargs): # Здесь вы можете добавить свой код перед вызовом функции val = self.func(*args, **kwargs) # Здесь вы можете добавить свой код после вызова функции return val
Давайте используем этот шаблон кода, чтобы написать декоратор , который будет добавлять теги к строке, возвращаемой функцией.
import functools class Emphasis: def __init__(self, func): functools.update_wrapper(self, func) self.func = func def __call__(self, *args, **kwargs): val = self.func(*args, **kwargs) return "<b>" + val + "</b>" @Emphasis def hello(name): return f"Hello {name}" print(hello("Nafi")) print(hello("Redowan"))
>>> <b>Hello Nafi</b> <b>Hello Redowan</b>
Метод сохраняет ссылку на функцию и может выполнять другой необходимый код инициализации. Метод будет вызываться вместо функции, к которой мы хотим применять декоратор. По сути, он делает то же самое, что и функция из наших предыдущих примеров
Обратите внимание, что в этом примере используется функция вместо
Прежде чем мы двигаться дальше, давайте определим декоратор с сохранением состояния, используя синтаксис класса. Декораторы с сохранением состояния могут запоминать его некоторое состояние от предыдущего запуска. В примере ниже декоратор с сохранением данных состояния в словаре, который следит за тем, сколько раз были вызваны декорируемые функции. Ключи этого словаря будут содержать имена соответствующих функций, а значения – количество их вызовов.
import functools class Tally: def __init__(self, func): functools.update_wrapper(self, func) self.func = func self.tally = {} self.n_calls = 0 def __call__(self, *args, **kwargs): self.n_calls += 1 self.tally = self.n_calls print("Callable Tally:", self.tally) return self.func(*args, **kwargs) @Tally def hello(name): return f"Hello {name}!" print(hello("Redowan")) print(hello("Nafi"))
>>> Callable Tally: {'hello': 1} Hello Redowan! Callable Tally: {'hello': 2} Hello Nafi!
Полиморфизм
Полиморфизм — это способность использовать в ООП общий интерфейс для нескольких форм (типов данных).
Предположим, что нужно раскрасить фигуру. Есть несколько вариантов фигуры (прямоугольник, квадрат, круг). Мы могли бы использовать тот же метод, чтобы закрасить любую форму. Эта концепция называется полиморфизмом.
Пример 5: Использование полиморфизма в Python
class Parrot: def fly(self): print("Parrot can fly") def swim(self): print("Parrot can't swim") class Penguin: def fly(self): print("Penguin can't fly") def swim(self): print("Penguin can swim") # общий интерфейс def flying_test(bird): bird.fly() #создаем объекты blu = Parrot() peggy = Penguin() # передаем объекты flying_test(blu) flying_test(peggy)
Результат:
Parrot can fly Penguin can't fly
Мы определили два класса: Parrot и Penguin . У каждого из них есть общий метод fly(), но они разные.
Чтобы реализовать полиморфизм, мы создали общий интерфейс. То есть, функцию flying_test(), которая может принимать любой объект. Затем мы передали объекты blu и peggy в функцию flying_test().
Наследование
Python поддерживает множественное наследование, то есть создание класса более чем от одного родителя.
Чтобы продемонстрировать это, разделим класс Car на две категории: одну – для транспортных средств и одну – для машин, использующих электричество:
В классе Vehicle определены атрибуты color и model. В классе Device имеется атрибут _voltage. Класс Car происходит от этих двух классов, и атрибуты color, model и _voltage теперь являются частью нового класса.
В методе init() класса Car вызываются методы init() обоих родительских классов, чтобы все данные проинициализировались должным образом. После этого мы можем добавить классу Car любую желаемую функциональность. В данном примере мы добавим атрибут year, а также геттер и сеттер для _voltage.
Функциональность нового класса Car осталась прежней. Мы можем создавать и использовать объекты класса, как это делали несколькими примерами ранее:
Язык Java же, в свою очередь, поддерживает только одиночное наследование, что означает, что классы в Java могут наследовать данные и поведение только от одного родительского класса. Зато в Java возможно наследование от множества интерфейсов. Интерфейсы обеспечивают группу связанных методов, которые нужно реализовать, позволяя дочерним классам вести себя сходным образом.
Чтобы увидеть это, разделим Java-класс Car на родительский класс и интерфейс:
Не забываем, что каждый класс и каждый интерфейс в Java должны быть размещены в своем собственном файле.
Как и в вышеприведенном примере с Python, мы создаем новый класс Vehicle для хранения общих данных и функционала, присущих транспортным средствам. Однако для добавления функциональных возможностей Device нам нужно создать интерфейс, определяющий метод получения напряжения (voltage) устройства.
Класс Car создается путем наследования от класса Vehicle с использованием ключевого слова extends и реализации интерфейса Device с использованием ключевого слова implements. В конструкторе класса мы вызываем конструктор родителя при помощи super(). Поскольку родительский класс только один, мы обращаемся к конструктору класса Vehicle. Для реализации интерфейса переопределяем getVoltage() с помощью аннотации Override.
Вместо повторного использования кода из Device, как это делается в Python, Java требует, чтобы мы реализовывали один и тот же функционал в каждом классе, который реализует интерфейс. Интерфейсы всего лишь определяют методы — они не могут определять данные экземпляра класса или детали реализации.
Так почему же это происходит с Java? Причина кроется в типах данных и проверке типов.
Что такое объектно-ориентированное программирование (ООП)?
Объектно-ориентированное программирование, или, если коротко, ООП , – это парадигма программирования, которая предоставляет средства структурирования программ таким образом, что свойства и поведение объединяются в отдельные объекты .
Например, объект может представлять человека с именем, возрастом, адресом и т. Д., С такими поведениями, как ходьба, разговор, дыхание и бег. Или электронное письмо со свойствами, такими как список получателей, тема, тело и т. Д., А также с такими поведениями, как добавление вложений и отправка.
Иными словами, объектно-ориентированное программирование – это подход для моделирования конкретных реальных вещей, таких как автомобили, а также отношений между такими вещами, как компании и сотрудники, студенты и преподаватели и т. Д. ООП моделирует реальные объекты как программные объекты, которые имеют некоторые данные связаны с ними и могут выполнять определенные функции.
Другой распространенной парадигмой программирования является процедурное программирование, которое структурирует программу подобно рецепту в том смысле, что она предоставляет набор шагов в форме функций и блоков кода, которые последовательно выполняются для выполнения задачи.
Ключевым выводом является то, что объекты находятся в центре парадигмы объектно-ориентированного программирования, представляя не только данные, как в процедурном программировании, но и в общей структуре программы.
Что показывает этот пример?
Этот пример демонстрирует приверженность логической последовательности.
Без парадигмы ООП мы бы назвали эти функции,, и так далее. Они не будут сгруппированы под общей логической единицей.
Зачем? Потому что они разные функции и работают с другим набором данных. В то времяфункция ожидает как тренировочные функции и цели,нужен только набор тестовых данных.функция не должна ничего возвращать, в то время каккак ожидается, вернет набор прогнозов
Итак, почему они видны под одним выпадающим? Несмотря на различие, они имеют общность, котораяоба они могут быть представлены как важные части общего процесса линейной регрессии- мы ожидаем, что линейная регрессия будет соответствовать некоторым обучающим данным, а затем сможем прогнозировать будущие невидимые данные. Мы также ожидаем, что модель линейной регрессии предоставит нам некоторое представление о том, насколько хорошо было соответствие — как правило, в форме единственной числовой величины или показателя, называемого коэффициентом регрессии или R². Как и ожидалось, мы видим функцию, который возвращает именно это число R², также висит вокруга также,
Аккуратно и чисто, не правда ли?
Атрибуты класса
Ранее мы поняли, как генерировать объекты класса и как они могут использоваться для получения доступа к определенным атрибутам класса.
В Python, каждый из объектов включает определенные атрибуты по умолчанию и методы в дополнение к конкретным атрибутам, созданным разработчиком. Чтобы получить перечень всех атрибутов и методов объекта, используется специальная функция dir().
Попробуем посмотреть на все атрибуты объекта car_b, созданногоранее. Выполните такой скрипт.
print(dir(car_b))
В выдаче вы увидите такие атрибуты.
[‘__class__’,
‘__delattr__’,
‘__dict__’,
‘__dir__’,
‘__doc__’,
‘__eq__’,
‘__format__’,
‘__ge__’,
‘__getattribute__’,
‘__gt__’,
‘__hash__’,
‘__init__’,
‘__init_subclass__’,
‘__le__’,
‘__lt__’,
‘__module__’,
‘__ne__’,
‘__new__’,
‘__reduce__’,
‘__reduce_ex__’,
‘__repr__’,
‘__setattr__’,
‘__sizeof__’,
‘__str__’,
‘__subclasshook__’,
‘__weakref__’,
‘make’,
‘model’,
‘name’,
‘start’,
‘stop’]
Данная встроенная функция будет очень полезной для изучения атрибутов и функций того или иного экземпляра. Особенно если используется REPL.
Атрибуты класса против атрибутов объектов
В целом, выделяют два типа атрибутов:
- Атрибуты класса.
- Атрибуты экземпляров.
Атрибуты класса делятся среди всех экземпляров, относящихся к определенному классу. В то же время, атрибуты экземпляров – это собственность экземпляра.
Атрибуты объекта объявляются внутри какого-угодно метода. В то же время, атрибуты класса объявляются за пределами любого метода.
Более точно понять эту разницу можно с помощью такого примера.
class Car: # создаем атрибуты класса car_count = 0 # создаем методы класса def start(self, name, make, model): print("Двигатель заведен") self.name = name self.make = make self.model = model Car.car_count += 1
Здесь нами создается класс Car, имеющий один атрибут класса, который называется car_count, а также три атрибута объекта, именуемых name, make, model. Класс включает один метод start(), в который входит три атрибута экземпляров. В свою очередь, значения атрибутов экземпляров передаются в качестве аргументов методу start(). Внутри метода start, атрибут car_count увеличивается на единицу.
Необходимо отдельно обозначить то, что в рамках метода, атрибуты экземпляра ссылаются с помощью ключевика self, в то время как атрибуты класса ссылаются с помощью его названия.
Давайте попробуем создать объект класса Car, после чего вызовем метод start().
car_a = Car() car_a.start("Corrola", "Toyota", 2015) print(car_a.name) print(car_a.car_count)
В приведенном ранее скрипте мы вывели название атрибута экземпляра и атрибута класса car_count. В выдаче будет показано, что атрибут car_count будет иметь значение 1, как указано ниже.
Двигатель заведен
Corrola
1
Теперь давайте попробуем создать еще один объект класса Car и вызвать метод start().
car_b = Car() car_b.start("City", "Honda", 2013) print(car_b.name) print(car_b.car_count)
Сейчас если вы выведете значение атрибута car_count, то в выдаче увидите цифру 2. Почему это так? Дело в том, что атрибут car_count – это атрибут класса. Следовательно, он разделяется между экземплярами. Объект car_a увеличил значение до единицы, в то время как car_b увеличил значение еще раз таким образом, что результирующее значение получается 2. Выдача выглядит так.
Двигатель заведен
City
2
Методы[править]
Методправить
Синтаксис описания метода ничем не отличается от описания функции, разве что его положением внутри класса и характерным первым формальным параметром , с помощью которого внутри метода можно ссылаться на сам экземпляр класса (название self является соглашением, которого придерживаются программисты на Python):
class MyClass(object): def mymethod(self, x): return x == self._x
Статический методправить
Статические методы в Python являются синтаксическими аналогами статических функций
в основных языках программирования. Они не получают ни экземпляр (),
ни класс () первым параметром.
Для создания статического метода (только классы могут иметь статические методы) используется
>>> class D(object): @staticmethod def test(x): return x == ... >>> D.test(1) # доступ к статическому методу можно получать и через класс False >>> f = D() >>> f.test() # и через экземпляр класса True
Статические методы реализованы с помощью
.
Метод классаправить
Классовые методы в Python занимают промежуточное положение между
статическими и обычными. В то время как обычные методы получают первым
параметром экземпляр класса, а статические не получают ничего, в классовые
методы передается класс. Возможность создания классовых методов является
одним из следствий того, что в Python классы также являются объектами.
Для создания классового (только классы могут иметь классовые методы) метода можно использовать
декоратор
>>> class A(object): def __init__(self, int_val): self.val = int_val + 1 @classmethod def fromString(cls, val): # вместо self принято использовать cls return cls(int(val)) ... >>> class B(A):pass ... >>> x = A.fromString("1") >>> print x.__class__.__name__ A >>> x = B.fromString("1") >>> print x.__class__.__name__ B
Классовые методы достаточно часто используются для перегрузки конструктора.
Классовые методы, как и статические, реализуются через
.
Мультиметодыправить
Примером для иллюстрации сути мультиметода может служить функция из модуля :
>>> import operator as op >>> print op.add(2, 2), op.add(2.0, 2), op.add(2, 2.0), op.add(2j, 2) 4 4.0 4.0 (2+2j)
from multimethods import Dispatch class Asteroid(object): pass class Spaceship(object): pass def asteroid_with_spaceship(a1, s1): print "A-><-S" def asteroid_with_asteroid(a1, a2): print "A-><-A" def spaceship_with_spaceship(s1, s2): print "S-><-S" collide = Dispatch() collide.add_rule((Asteroid, Spaceship), asteroid_with_spaceship) collide.add_rule((Asteroid, Asteroid), asteroid_with_asteroid) collide.add_rule((Spaceship, Spaceship), spaceship_with_spaceship) collide.add_rule((Spaceship, Asteroid), lambda x,y asteroid_with_spaceship(y,x)) a, s1, s2 = Asteroid(), Spaceship(), Spaceship() collision1 = collide(a, s1)[ collision2 = collide(s1, s2)[
Наследование
В организации наследования участвуют как минимум два класса: класс родитель и класс потомок. При этом возможно множественное наследование, в этом случае у класса потомка может быть несколько родителей. Не все языки программирования поддерживают множественное наследование, но в Python можно его использовать. По умолчанию все классы в Python являются наследниками от object, явно этот факт указывать не нужно.
Синтаксически создание класса с указанием его родителя выглядит так:
class имя_класса(имя_родителя1, )
Переработаем наш пример так, чтобы в нем присутствовало наследование:
class Figure: def __init__(self, color): self.__color = color @property def color(self): return self.__color @color.setter def color(self, c): self.__color = c class Rectangle(Figure): def __init__(self, width, height, color): super().__init__(color) self.__width = width self.__height = height @property def width(self): return self.__width @width.setter def width(self, w): if w > 0: self.__width = w else: raise ValueError @property def height(self): return self.__height @height.setter def height(self, h): if h > 0: self.__height = h else: raise ValueError def area(self): return self.__width * self.__height
Родительским классом является Figure, который при инициализации принимает цвет фигуры и предоставляет его через свойства. Rectangle – класс наследник от Figure
Обратите внимание на его метод __init__: в нем первым делом вызывается конструктор (хотя это не совсем верно, но будем говорить так) его родительского класса:
super().__init__(color)
super – это ключевое слово, которое используется для обращения к родительскому классу.
Теперь у объекта класса Rectangle помимо уже знакомых свойств width и height появилось свойство color:
>>> rect = Rectangle(10, 20, "green") >>> rect.width 10 >>> rect.height 20 >>> rect.color 'green' >>> rect.color = "red" >>> rect.color 'red'
Объекты Python (экземпляры класса)
В то время как класс является шаблоном, экземпляр является реализацией класса с фактическими значениями, буквально объектом, принадлежащим определенному классу. Это больше не идея; это реальное животное, как собака по имени Роджер, которой восемь лет.
Иными словами, класс — это форма или шаблон. Он определяет необходимую информацию. После того, как вы заполните форму, ваша конкретная копия становится экземпляром класса; он содержит актуальную информацию, относящуюся к вам.
Вы можете заполнить несколько копий, чтобы создать много разных экземпляров, но без формы в качестве руководства вы потерялись бы, не зная, какая информация требуется. Таким образом, прежде чем вы сможете создавать отдельные экземпляры объекта, мы должны сначала указать, что нужно, определив класс.
наследовать
Дочерний класс может наследовать все свойства и методы родительского класса и родительского класса.
Формат наследования:
Перепишите метод: Если подкласс не удовлетворяет требованиям, он также может переписать свойства и методы родительского класса. Возможны два случая:
- Перезапись переопределяет метод родительского класса: просто напишите функцию с тем же именем для перезаписи.
- Extend расширяет функцию родительского класса:
- Первый способ (основной): написать функцию с таким же именем и передать ееОбратитесь к методу родительского класса. из ихЭто специальный класс, встроенный в python, иТо есть создается экземпляр super. Создайте суперэкземпляр в подклассе и получите ссылку на родительский класс.
- Второй способ (использовался до python 2.x): написать функцию с тем же именем, а затем передатьОбратитесь к методу родительского класса. Но это не рекомендуется, потому что при изменении имени родительского класса необходимо изменить все подклассы.
Личное без наследства: Дочерний класс может наследовать только общедоступное содержимое родительского класса, ноНе включеноЛичное содержимое родительского класса. Если вы хотите получить к нему доступ, вы можете, но вам нужно косвенно вызвать родительский класс, а затем использовать метод для вызова частного содержимого.
Множественное наследование
В Python подклассы могут иметь несколько родительских классов одновременно: то есть они могут наследовать все атрибуты и методы нескольких родительских классов одновременно.
Формат наследования:
заметка: Если несколько родительских классов имеют методы с одинаковыми именами, они наследуютПервыйМетод родительского класса.
Посмотреть порядок наследования: поставляется с классомАтрибуты (), вы можете просмотреть порядок наследования этого класса.
Подкласс можно написать напрямуюДля вызова родительской функции. Но при использовании в качестве подклассаВремя, питон будет основан наПоследовательность, поиск от ближнего к дальнему и возврат к ближайшему начальнику.
Используя приведенный выше пример, если это множественное наследование, порядок поиска следующий:。
Базовый класс встроенного объекта Python
Python3 начал использовать определения классов нового стиля, то есть по умолчанию все определенные классы автоматически наследуют вызовВстроенные базовые классы.Базовый класс определяет множество удобных атрибутов. В том числе аж 18 наименований. В старой эре Python 2.x базовый класс объекта не наследуется, и единственный класс, определенный сам по себе, -с участиемВсего два встроенных свойства. В эпоху 2.x, если вам нужно наследовать вручную, например:
Инкапсуляция
Под инкапсуляцией понимается сокрытие деталей реализации, данных и т.п. от внешней стороны. Например, можно определить класс “холодильник”, который будет содержать следующие данные: производитель, объем, количество камер хранения, потребляемая мощность и т.п., и методы: открыть/закрыть холодильник, включить/выключить, но при этом реализация того, как происходит непосредственно включение и выключение пользователю вашего класса не доступна, что позволяет ее менять без опасения, что это может отразиться на использующей класс «холодильник» программе. При этом класс становится новым типом данных в рамках разрабатываемой программы. Можно создавать переменные этого нового типа, такие переменные называются объекты.
Наследование классов
Вместо того чтобы начать с нуля, вы можете создать класс, выводя его из предварительно существующего класса, перечислив родительский класс в скобках после нового имени класса.
Дочерний класс наследует атрибуты своего родительского класса, и вы можете использовать эти атрибуты, как если бы они были определены в дочернем классе. Дочерний класс может также переопределить элементы данных и методы от родителей.
Синтаксис
Производные классы объявляются так же, как их родительский класс; Однако, список базовых классов наследуются после имени класса:
class SubClassName (ParentClass1): 'Необязательная строка документации класса' class_suite
Пример
#!/usr/bin/python3 class Parent: # определить родительский класс parentAttr = 100 def __init__(self): print ("Вызов родительского конструктора") def parentMethod(self): print ('Вызов родительского метода') def setAttr(self, attr): Parent.parentAttr = attr def getAttr(self): print ("Родительский атрибут :", Parent.parentAttr) class Child(Parent): # определить дочерний класс def __init__(self): print ("Вызов дочернего конструктора") def childMethod(self): print ('Вызов дочернего метода') c = Child() # дочерний экземпляр c.childMethod() # дочерний экземпляр называет свой метод c.parentMethod() # вызывает родительский метод c.setAttr(200) # снова вызов родительского метода c.getAttr() # снова вызов родительского метода
Когда этот код выполнится, он выведет следующий результат:
Вызов дочернего конструктора Вызов дочернего метода Вызов родительского метода Родительский атрибут : 200
Аналогичным образом, вы можете управлять классом от нескольких родительских классов следующим образом:
class A: # определите свой класс A ..... class B: # определите свой класс B ..... class C(A, B): # Субкласс A и B .....
Вы можете использовать функции issubclass() или isinstance() для проверки отношения двух классов и экземпляров.
- Функция issubclass(sub, sup) возвращает логическое значение true, если данный подкласс sub действительно подкласс суперкласса sup.
- Функция isinstance(obj, Class) возвращает логическое true, если obj является экземпляром класса Class или является экземпляром подкласса Class.
Занятия на Python
Сосредоточившись сначала на данных, каждая вещь или объект является экземпляром некоторого класса .
Примитивные структуры данных, доступные в Python, такие как числа, строки и списки, предназначены для представления простых вещей, таких как стоимость чего-либо, название стихотворения и ваши любимые цвета соответственно.
Что если вы хотите представить что-то гораздо более сложное?
Например, допустим, вы хотели отследить несколько разных животных. Если вы используете список, первый элемент может быть именем животного, в то время как второй элемент может представлять его возраст.
Как бы вы узнали, какой элемент должен быть? Что делать, если у вас было 100 разных животных? Вы уверены, что у каждого животного есть и имя, и возраст, и так далее? Что если вы захотите добавить другие свойства этим животным? Это не хватает организации, и это именно то, что нужно для занятий .
Классы используются для создания новых пользовательских структур данных, которые содержат произвольную информацию о чем-либо. В случае с животным мы могли бы создать класс для отслеживания таких свойств животного, как имя и возраст.
Важно отметить, что класс просто обеспечивает структуру – это образец того, как что-то должно быть определено, но на самом деле он не предоставляет никакого реального контента. Класс может указать , что имя и возраст, необходимые для определения животного, но он не будет на самом деле утверждать , что имя или возраст конкретного животного является. Это может помочь представить класс как идею того, как что-то должно быть определено
Это может помочь представить класс как идею того, как что-то должно быть определено.
Методы и функции
Разница между рассматриваемыми языками заключается в том, что в Python есть функции, а в Java их нет.
В Python следующий код отработает без проблем (и используется повсеместно):
Мы можем вызвать say_hi() из любого места видимости. Эта функция не содержит ссылки на self, что означает, что это глобальная функция, а не функция класса. Она не сможет изменять или сохранять какие-нибудь данные какого-либо класса, но может использовать локальные и глобальные переменные.
В противоположность, каждая написанная нами строчка на Java принадлежит какому-нибудь классу. Функции не существует за пределами класса, и по определению все Java-функции — это методы. На Java ближе всего к чистой функции находится статичный метод:
Utils. SayHi() вызывается из любого места без предварительного создания экземпляра класса Utils. Поскольку мы вызываем SayHi() без создания объекта, ссылки this не существует. Однако, это всё равно не функция в том смысле, в котором является say_hi() в Python.
Изучение атрибутов объекта
В Python при помощи dir() мы видим все атрибуты и функции, содержащиеся в объекте (включая магические методы). Чтобы получить конкретные сведения о данном атрибуте или функции, используем getattr():
В Java имеются аналогичные возможности, однако контроль доступа и типобезопасность, заложенные в языке, усложняют дело.
getFields() извлекает список всех общедоступных атрибутов. Однако, поскольку ни один из атрибутов класса Car не является публичным, этот код возвращает пустой массив:
Java рассматривает атрибуты и методы как отдельные сущности, поэтому публичные методы извлекаются при помощи getDeclaredMethods(). Поскольку публичные атрибуты будут иметь соответствующий get-метод, один из способов обнаружить, что класс содержит определенное свойство, может выглядеть таким образом:
1) использовать getDeclaredMethods() для генерации массива всех методов
2) перебрать все эти методы:
- для каждого обнаруженного метода вернуть true, если метод:
- в противном случае вернуть false.
Вот пример на скорую руку:
getProperty() – это точка входа. Вызовем ее с именем атрибута и объекта. Она вернет true, если свойство будет найдено, иначе вернет false.