Полиморфизм против переопределения против перегрузки
С точки зрения Java, когда кто-то спрашивает:
что такое полиморфизм?
Будет ли перегрузка или перегрузка приемлемым ответом?
Я думаю, что есть нечто большее, чем это.
Если у вас есть абстрактный базовый класс, который определил метод без реализации, и вы определили этот метод в подклассе, он все еще переопределяется?
Я думаю, что перегрузка не правильный ответ наверняка.
23 ответа
Самый простой способ выразить полиморфизм - через абстрактный базовый класс (или интерфейс)
public abstract class Human{
...
public abstract void goPee();
}
Этот класс является абстрактным, потому что goPee()
метод не определим для людей. Это определимо только для подклассов мужского и женского пола. Кроме того, Человек - это абстрактное понятие. Вы не можете создать человека, который не является ни мужчиной, ни женщиной. Это должен быть один или другой.
Поэтому мы откладываем реализацию, используя абстрактный класс.
public class Male extends Human{
...
@Override
public void goPee(){
System.out.println("Stand Up");
}
}
а также
public class Female extends Human{
...
@Override
public void goPee(){
System.out.println("Sit Down");
}
}
Теперь мы можем сказать всю комнату, полную людей, чтобы пописать.
public static void main(String[] args){
ArrayList<Human> group = new ArrayList<Human>();
group.add(new Male());
group.add(new Female());
// ... add more...
// tell the class to take a pee break
for (Human person : group) person.goPee();
}
Запуск этого даст:
Stand Up
Sit Down
...
Полиморфизм - это способность экземпляра класса вести себя так, как если бы он был экземпляром другого класса в своем дереве наследования, чаще всего одного из его классов-предков. Например, в Java все классы наследуются от Object. Следовательно, вы можете создать переменную типа Object и присвоить ей экземпляр любого класса.
Переопределение - это тип функции, которая происходит в классе, который наследуется от другого класса. Функция переопределения "заменяет" функцию, унаследованную от базового класса, но делает это таким образом, что она вызывается, даже когда экземпляр ее класса притворяется, что это другой тип посредством полиморфизма. Обращаясь к предыдущему примеру, вы можете определить свой собственный класс и переопределить функцию toString(). Поскольку эта функция наследуется от Object, она все равно будет доступна, если вы скопируете экземпляр этого класса в переменную Object-type. Обычно, если вы вызываете toString() для вашего класса, когда он притворяется объектом Object, то версия toString, которая на самом деле будет вызывать это та, которая определена для самого Object. Однако, поскольку функция является переопределением, определение toString() из вашего класса используется, даже когда истинный тип экземпляра класса скрыт за полиморфизмом.
Перегрузка - это действие определения нескольких методов с одинаковым именем, но с разными параметрами. Это не связано ни с переопределением, ни с полиморфизмом.
Полиморфизм означает более одной формы, один и тот же объект, выполняющий различные операции в соответствии с требованием.
Полиморфизм может быть достигнут с помощью двух способов, те, которые
- Переопределение метода
- Перегрузка метода
Перегрузка методов означает написание двух или более методов в одном и том же классе с использованием одного и того же имени метода, но передаваемые параметры отличаются.
Переопределение метода означает, что мы используем имена методов в разных классах, это означает, что метод родительского класса используется в дочернем классе.
В Java для достижения полиморфизма ссылочная переменная суперкласса может содержать объект подкласса.
Для достижения полиморфизма каждый разработчик должен использовать одинаковые имена методов в проекте.
Как переопределение, так и перегрузка используются для достижения полиморфизма.
Вы можете иметь метод в классе, который переопределяется в одном или нескольких подклассах. Метод делает разные вещи в зависимости от того, какой класс был использован для создания объекта.
abstract class Beverage {
boolean isAcceptableTemperature();
}
class Coffee extends Beverage {
boolean isAcceptableTemperature() {
return temperature > 70;
}
}
class Wine extends Beverage {
boolean isAcceptableTemperature() {
return temperature < 10;
}
}
Вы также можете иметь метод, который перегружен двумя или более наборами аргументов. Метод делает разные вещи в зависимости от типа (ов) переданного аргумента (ов).
class Server {
public void pour (Coffee liquid) {
new Cup().fillToTopWith(liquid);
}
public void pour (Wine liquid) {
new WineGlass().fillHalfwayWith(liquid);
}
public void pour (Lemonade liquid, boolean ice) {
Glass glass = new Glass();
if (ice) {
glass.fillToTopWith(new Ice());
}
glass.fillToTopWith(liquid);
}
}
Вот пример полиморфизма в псевдо-C#/Java:
class Animal
{
abstract string MakeNoise ();
}
class Cat : Animal {
string MakeNoise () {
return "Meow";
}
}
class Dog : Animal {
string MakeNoise () {
return "Bark";
}
}
Main () {
Animal animal = Zoo.GetAnimal ();
Console.WriteLine (animal.MakeNoise ());
}
Функция Main не знает тип животного и зависит от поведения конкретной реализации метода MakeNoise().
Изменить: Похоже, Брайан избил меня до удара. Забавно, мы использовали один и тот же пример. Но приведенный выше код должен помочь прояснить концепции.
Вы правы, что перегрузка не является ответом.
Ни один не переопределяет. Переопределение - это средство, с помощью которого вы получаете полиморфизм. Полиморфизм - это способность объекта варьировать поведение в зависимости от его типа. Это лучше всего демонстрируется, когда вызывающий объект, который проявляет полиморфизм, не знает, какой конкретно тип объекта.
Конкретное высказывание о перегрузке или переопределении не дает полной картины. Полиморфизм - это просто способность объекта специализировать свое поведение в зависимости от его типа.
Я бы не согласился с некоторыми ответами здесь в том, что перегрузка является формой полиморфизма (параметрического полиморфизма) в том случае, если метод с одним и тем же именем может вести себя по-разному, давая разные типы параметров. Хорошим примером является перегрузка операторов. Вы можете определить "+", чтобы принимать различные типы параметров - например, строки или целые числа - и на основе этих типов "+" будет вести себя по-разному.
Полиморфизм также включает методы наследования и переопределения, хотя они могут быть абстрактными или виртуальными в базовом типе. С точки зрения основанного на наследовании полиморфизма, Java поддерживает только наследование одного класса, ограничивая его полиморфное поведение до поведения одной цепочки базовых типов. Java поддерживает реализацию нескольких интерфейсов, что является еще одной формой полиморфного поведения.
Хотя Полиморфизм уже подробно описан в этом посте, но я бы хотел больше подчеркнуть, почему его часть.
Почему полиморфизм так важен в любом языке ООП.
Давайте попробуем создать простое приложение для телевизора с наследованием / полиморфизмом и без него. Размещая каждую версию приложения, мы делаем небольшую ретроспективу.
Предположим, вы инженер по программному обеспечению в телевизионной компании, и вас попросили написать программное обеспечение для контроллеров громкости, яркости и цвета для увеличения и уменьшения их значений по команде пользователя.
Вы начинаете с написания классов для каждой из этих функций, добавляя
- set:- для установки значения контроллера.(Предположим, что для этого есть специальный код контроллера)
- get: - Чтобы получить значение контроллера.(Предположим, это имеет специальный код контроллера)
- настроить: - Для проверки ввода и настройки контроллера.(Общие проверки.. независимо от контроллеров)
- Отображение пользовательского ввода с контроллерами: - Чтобы получить пользовательский ввод и соответственно вызывать контроллеры.
Версия приложения 1
import java.util.Scanner;
class VolumeControllerV1 {
private int value;
int get() {
return value;
}
void set(int value) {
System.out.println("Old value of VolumeController \t"+this.value);
this.value = value;
System.out.println("New value of VolumeController \t"+this.value);
}
void adjust(int value) {
int temp = this.get();
if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0))) {
System.out.println("Can not adjust any further");
return;
}
this.set(temp + value);
}
}
class BrightnessControllerV1 {
private int value;
int get() {
return value;
}
void set(int value) {
System.out.println("Old value of BrightnessController \t"+this.value);
this.value = value;
System.out.println("New value of BrightnessController \t"+this.value);
}
void adjust(int value) {
int temp = this.get();
if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0))) {
System.out.println("Can not adjust any further");
return;
}
this.set(temp + value);
}
}
class ColourControllerV1 {
private int value;
int get() {
return value;
}
void set(int value) {
System.out.println("Old value of ColourController \t"+this.value);
this.value = value;
System.out.println("New value of ColourController \t"+this.value);
}
void adjust(int value) {
int temp = this.get();
if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0))) {
System.out.println("Can not adjust any further");
return;
}
this.set(temp + value);
}
}
/*
* There can be n number of controllers
* */
public class TvApplicationV1 {
public static void main(String[] args) {
VolumeControllerV1 volumeControllerV1 = new VolumeControllerV1();
BrightnessControllerV1 brightnessControllerV1 = new BrightnessControllerV1();
ColourControllerV1 colourControllerV1 = new ColourControllerV1();
OUTER: while(true) {
Scanner sc=new Scanner(System.in);
System.out.println(" Enter your option \n Press 1 to increase volume \n Press 2 to decrease volume");
System.out.println(" Press 3 to increase brightness \n Press 4 to decrease brightness");
System.out.println(" Press 5 to increase color \n Press 6 to decrease color");
System.out.println("Press any other Button to shutdown");
int button = sc.nextInt();
switch (button) {
case 1: {
volumeControllerV1.adjust(5);
break;
}
case 2: {
volumeControllerV1.adjust(-5);
break;
}
case 3: {
brightnessControllerV1.adjust(5);
break;
}
case 4: {
brightnessControllerV1.adjust(-5);
break;
}
case 5: {
colourControllerV1.adjust(5);
break;
}
case 6: {
colourControllerV1.adjust(-5);
break;
}
default:
System.out.println("Shutting down...........");
break OUTER;
}
}
}
}
Теперь у вас есть первая версия рабочего приложения, готовая к развертыванию. Время проанализировать проделанную работу.
Проблемы в ТВ-версии приложения 1
- Код Adjust(int value) повторяется во всех трех классах. Вы хотели бы минимизировать дублирование кода.(Но вы не думали об общем коде и перемещении его в какой-то суперкласс, чтобы избежать дублирования кода)
Вы решаете жить с этим, пока ваше приложение работает, как ожидалось.
Через некоторое время ваш босс возвращается к вам и просит добавить функцию сброса в существующее приложение. Сброс установит все три контроллера на их соответствующие значения по умолчанию.
Вы начинаете писать новый класс (ResetFunctionV2) для новой функциональности и сопоставляете код отображения пользовательского ввода для этой новой функции.
Версия приложения 2
import java.util.Scanner;
class VolumeControllerV2 {
private int defaultValue = 25;
private int value;
int getDefaultValue() {
return defaultValue;
}
int get() {
return value;
}
void set(int value) {
System.out.println("Old value of VolumeController \t"+this.value);
this.value = value;
System.out.println("New value of VolumeController \t"+this.value);
}
void adjust(int value) {
int temp = this.get();
if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0))) {
System.out.println("Can not adjust any further");
return;
}
this.set(temp + value);
}
}
class BrightnessControllerV2 {
private int defaultValue = 50;
private int value;
int get() {
return value;
}
int getDefaultValue() {
return defaultValue;
}
void set(int value) {
System.out.println("Old value of BrightnessController \t"+this.value);
this.value = value;
System.out.println("New value of BrightnessController \t"+this.value);
}
void adjust(int value) {
int temp = this.get();
if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0))) {
System.out.println("Can not adjust any further");
return;
}
this.set(temp + value);
}
}
class ColourControllerV2 {
private int defaultValue = 40;
private int value;
int get() {
return value;
}
int getDefaultValue() {
return defaultValue;
}
void set(int value) {
System.out.println("Old value of ColourController \t"+this.value);
this.value = value;
System.out.println("New value of ColourController \t"+this.value);
}
void adjust(int value) {
int temp = this.get();
if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0))) {
System.out.println("Can not adjust any further");
return;
}
this.set(temp + value);
}
}
class ResetFunctionV2 {
private VolumeControllerV2 volumeControllerV2 ;
private BrightnessControllerV2 brightnessControllerV2;
private ColourControllerV2 colourControllerV2;
ResetFunctionV2(VolumeControllerV2 volumeControllerV2, BrightnessControllerV2 brightnessControllerV2, ColourControllerV2 colourControllerV2) {
this.volumeControllerV2 = volumeControllerV2;
this.brightnessControllerV2 = brightnessControllerV2;
this.colourControllerV2 = colourControllerV2;
}
void onReset() {
volumeControllerV2.set(volumeControllerV2.getDefaultValue());
brightnessControllerV2.set(brightnessControllerV2.getDefaultValue());
colourControllerV2.set(colourControllerV2.getDefaultValue());
}
}
/*
* so on
* There can be n number of controllers
*
* */
public class TvApplicationV2 {
public static void main(String[] args) {
VolumeControllerV2 volumeControllerV2 = new VolumeControllerV2();
BrightnessControllerV2 brightnessControllerV2 = new BrightnessControllerV2();
ColourControllerV2 colourControllerV2 = new ColourControllerV2();
ResetFunctionV2 resetFunctionV2 = new ResetFunctionV2(volumeControllerV2, brightnessControllerV2, colourControllerV2);
OUTER: while(true) {
Scanner sc=new Scanner(System.in);
System.out.println(" Enter your option \n Press 1 to increase volume \n Press 2 to decrease volume");
System.out.println(" Press 3 to increase brightness \n Press 4 to decrease brightness");
System.out.println(" Press 5 to increase color \n Press 6 to decrease color");
System.out.println(" Press 7 to reset TV \n Press any other Button to shutdown");
int button = sc.nextInt();
switch (button) {
case 1: {
volumeControllerV2.adjust(5);
break;
}
case 2: {
volumeControllerV2.adjust(-5);
break;
}
case 3: {
brightnessControllerV2.adjust(5);
break;
}
case 4: {
brightnessControllerV2.adjust(-5);
break;
}
case 5: {
colourControllerV2.adjust(5);
break;
}
case 6: {
colourControllerV2.adjust(-5);
break;
}
case 7: {
resetFunctionV2.onReset();
break;
}
default:
System.out.println("Shutting down...........");
break OUTER;
}
}
}
}
Итак, ваше приложение готово с функцией сброса. Но теперь вы начинаете понимать, что
Проблемы в телевизионном приложении версии 2
- Если новый продукт введен в продукт, вы должны изменить код функции сброса.
- Если количество контроллеров растет очень высоко, у вас могут возникнуть проблемы с удержанием ссылок на контроллеры.
- Сброс кода функции тесно связан со всем кодом класса контроллеров (для получения и установки значений по умолчанию).
- Сброс класса объектов (ResetFunctionV2) может получить доступ к другому методу класса Controller (настроить), что нежелательно.
В то же время вы слышите от своего босса, что вам, возможно, придется добавить функцию, при которой каждому контроллеру при запуске необходимо проверять наличие последней версии драйвера из размещенного в компании хранилища драйверов через Интернет.
Теперь вы начинаете думать, что эта новая функция, которая будет добавлена, напоминает функцию "Сброс", и "Проблемы приложения" (V2) будут умножены, если вы не пересчитаете свое приложение.
Вы начинаете думать об использовании наследования, чтобы воспользоваться преимуществами полиморфной способности JAVA и добавить новый абстрактный класс (ControllerV3) в
- Объявите подпись метода get и set.
- Содержит настройку реализации метода, которая ранее была реплицирована среди всех контроллеров.
- Объявите метод setDefault, чтобы можно было легко реализовать функцию сброса, используя полиморфизм.
С этими улучшениями у вас есть готовая версия 3 вашего телевизионного приложения.
Версия приложения 3
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
abstract class ControllerV3 {
abstract void set(int value);
abstract int get();
void adjust(int value) {
int temp = this.get();
if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0))) {
System.out.println("Can not adjust any further");
return;
}
this.set(temp + value);
}
abstract void setDefault();
}
class VolumeControllerV3 extends ControllerV3 {
private int defaultValue = 25;
private int value;
public void setDefault() {
set(defaultValue);
}
int get() {
return value;
}
void set(int value) {
System.out.println("Old value of VolumeController \t"+this.value);
this.value = value;
System.out.println("New value of VolumeController \t"+this.value);
}
}
class BrightnessControllerV3 extends ControllerV3 {
private int defaultValue = 50;
private int value;
public void setDefault() {
set(defaultValue);
}
int get() {
return value;
}
void set(int value) {
System.out.println("Old value of BrightnessController \t"+this.value);
this.value = value;
System.out.println("New value of BrightnessController \t"+this.value);
}
}
class ColourControllerV3 extends ControllerV3 {
private int defaultValue = 40;
private int value;
public void setDefault() {
set(defaultValue);
}
int get() {
return value;
}
void set(int value) {
System.out.println("Old value of ColourController \t"+this.value);
this.value = value;
System.out.println("New value of ColourController \t"+this.value);
}
}
class ResetFunctionV3 {
private List<ControllerV3> controllers = null;
ResetFunctionV3(List<ControllerV3> controllers) {
this.controllers = controllers;
}
void onReset() {
for (ControllerV3 controllerV3 :this.controllers) {
controllerV3.setDefault();
}
}
}
/*
* so on
* There can be n number of controllers
*
* */
public class TvApplicationV3 {
public static void main(String[] args) {
VolumeControllerV3 volumeControllerV3 = new VolumeControllerV3();
BrightnessControllerV3 brightnessControllerV3 = new BrightnessControllerV3();
ColourControllerV3 colourControllerV3 = new ColourControllerV3();
List<ControllerV3> controllerV3s = new ArrayList<>();
controllerV3s.add(volumeControllerV3);
controllerV3s.add(brightnessControllerV3);
controllerV3s.add(colourControllerV3);
ResetFunctionV3 resetFunctionV3 = new ResetFunctionV3(controllerV3s);
OUTER: while(true) {
Scanner sc=new Scanner(System.in);
System.out.println(" Enter your option \n Press 1 to increase volume \n Press 2 to decrease volume");
System.out.println(" Press 3 to increase brightness \n Press 4 to decrease brightness");
System.out.println(" Press 5 to increase color \n Press 6 to decrease color");
System.out.println(" Press 7 to reset TV \n Press any other Button to shutdown");
int button = sc.nextInt();
switch (button) {
case 1: {
volumeControllerV3.adjust(5);
break;
}
case 2: {
volumeControllerV3.adjust(-5);
break;
}
case 3: {
brightnessControllerV3.adjust(5);
break;
}
case 4: {
brightnessControllerV3.adjust(-5);
break;
}
case 5: {
colourControllerV3.adjust(5);
break;
}
case 6: {
colourControllerV3.adjust(-5);
break;
}
case 7: {
resetFunctionV3.onReset();
break;
}
default:
System.out.println("Shutting down...........");
break OUTER;
}
}
}
}
Хотя большинство проблем, перечисленных в списке проблем V2, были решены, за исключением
Проблемы в ТВ-версии приложения 3
- Сброс класса объектов (ResetFunctionV3) может получить доступ к другому методу класса Controller (настроить), что нежелательно.
Опять же, вы думаете о решении этой проблемы, так как теперь у вас есть еще одна функция (обновление драйвера при запуске), которая также будет реализована. Если вы не исправите это, он также будет перенесен на новые функции.
Таким образом, вы разделяете контракт, определенный в абстрактном классе, и пишете 2 интерфейса для
- Сбросить функцию.
- Обновление драйвера.
И пусть ваш первый конкретный класс реализует их, как показано ниже
Версия приложения 4
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
interface OnReset {
void setDefault();
}
interface OnStart {
void checkForDriverUpdate();
}
abstract class ControllerV4 implements OnReset,OnStart {
abstract void set(int value);
abstract int get();
void adjust(int value) {
int temp = this.get();
if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0))) {
System.out.println("Can not adjust any further");
return;
}
this.set(temp + value);
}
}
class VolumeControllerV4 extends ControllerV4 {
private int defaultValue = 25;
private int value;
@Override
int get() {
return value;
}
void set(int value) {
System.out.println("Old value of VolumeController \t"+this.value);
this.value = value;
System.out.println("New value of VolumeController \t"+this.value);
}
@Override
public void setDefault() {
set(defaultValue);
}
@Override
public void checkForDriverUpdate() {
System.out.println("Checking driver update for VolumeController .... Done");
}
}
class BrightnessControllerV4 extends ControllerV4 {
private int defaultValue = 50;
private int value;
@Override
int get() {
return value;
}
@Override
void set(int value) {
System.out.println("Old value of BrightnessController \t"+this.value);
this.value = value;
System.out.println("New value of BrightnessController \t"+this.value);
}
@Override
public void setDefault() {
set(defaultValue);
}
@Override
public void checkForDriverUpdate() {
System.out.println("Checking driver update for BrightnessController .... Done");
}
}
class ColourControllerV4 extends ControllerV4 {
private int defaultValue = 40;
private int value;
@Override
int get() {
return value;
}
void set(int value) {
System.out.println("Old value of ColourController \t"+this.value);
this.value = value;
System.out.println("New value of ColourController \t"+this.value);
}
@Override
public void setDefault() {
set(defaultValue);
}
@Override
public void checkForDriverUpdate() {
System.out.println("Checking driver update for ColourController .... Done");
}
}
class ResetFunctionV4 {
private List<OnReset> controllers = null;
ResetFunctionV4(List<OnReset> controllers) {
this.controllers = controllers;
}
void onReset() {
for (OnReset onreset :this.controllers) {
onreset.setDefault();
}
}
}
class InitializeDeviceV4 {
private List<OnStart> controllers = null;
InitializeDeviceV4(List<OnStart> controllers) {
this.controllers = controllers;
}
void initialize() {
for (OnStart onStart :this.controllers) {
onStart.checkForDriverUpdate();
}
}
}
/*
* so on
* There can be n number of controllers
*
* */
public class TvApplicationV4 {
public static void main(String[] args) {
VolumeControllerV4 volumeControllerV4 = new VolumeControllerV4();
BrightnessControllerV4 brightnessControllerV4 = new BrightnessControllerV4();
ColourControllerV4 colourControllerV4 = new ColourControllerV4();
List<ControllerV4> controllerV4s = new ArrayList<>();
controllerV4s.add(brightnessControllerV4);
controllerV4s.add(volumeControllerV4);
controllerV4s.add(colourControllerV4);
List<OnStart> controllersToInitialize = new ArrayList<>();
controllersToInitialize.addAll(controllerV4s);
InitializeDeviceV4 initializeDeviceV4 = new InitializeDeviceV4(controllersToInitialize);
initializeDeviceV4.initialize();
List<OnReset> controllersToReset = new ArrayList<>();
controllersToReset.addAll(controllerV4s);
ResetFunctionV4 resetFunctionV4 = new ResetFunctionV4(controllersToReset);
OUTER: while(true) {
Scanner sc=new Scanner(System.in);
System.out.println(" Enter your option \n Press 1 to increase volume \n Press 2 to decrease volume");
System.out.println(" Press 3 to increase brightness \n Press 4 to decrease brightness");
System.out.println(" Press 5 to increase color \n Press 6 to decrease color");
System.out.println(" Press 7 to reset TV \n Press any other Button to shutdown");
int button = sc.nextInt();
switch (button) {
case 1: {
volumeControllerV4.adjust(5);
break;
}
case 2: {
volumeControllerV4.adjust(-5);
break;
}
case 3: {
brightnessControllerV4.adjust(5);
break;
}
case 4: {
brightnessControllerV4.adjust(-5);
break;
}
case 5: {
colourControllerV4.adjust(5);
break;
}
case 6: {
colourControllerV4.adjust(-5);
break;
}
case 7: {
resetFunctionV4.onReset();
break;
}
default:
System.out.println("Shutting down...........");
break OUTER;
}
}
}
}
Теперь все проблемы, с которыми вы столкнулись, были решены, и вы поняли, что с помощью наследования и полиморфизма вы можете
- Сохраняйте слабую связь между различными частями приложения.(Компонентам "Сброс" или "Обновление драйверов" не нужно знать о фактических классах контроллера (Громкость, Яркость и Цвет)), любой класс, реализующий OnReset или OnStart, будет приемлем для функции "Сброс" или "Обновление драйверов". компоненты соответственно).
- Улучшение приложения становится проще.(Новое добавление контроллера не влияет на сброс или компонент обновления драйверов, и теперь вам действительно легко добавлять новые)
- Сохраняйте уровень абстракции.(Теперь функция Reset может видеть только метод контроллеров setDefault, а функция Reset может видеть только метод контроллеров checkForDriverUpdate)
Надеюсь это поможет:-)
Полиморфизм просто означает "много форм".
Это НЕ ТРЕБУЕТ наследования для достижения... поскольку реализация интерфейса, которая вообще не является наследованием, служит полиморфным потребностям. Возможно, реализация интерфейса удовлетворяет полиморфные потребности "лучше", чем наследование.
Например, вы бы создали супер-класс, чтобы описать все, что может летать? Я должен думать нет. Вам лучше всего создать интерфейс, описывающий полет, и оставить все как есть.
Таким образом, поскольку интерфейсы описывают поведение, а имена методов описывают поведение (для программиста), не слишком сложно рассматривать перегрузку методов как меньшую форму полиморфизма.
Классический пример, собаки и кошки - животные, у животных есть метод makeNoise. Я могу перебрать массив животных, вызывающих на них makeNoise, и ожидать, что они выполнят соответствующую реализацию.
Код вызова не должен знать, что это за конкретное животное.
Вот что я считаю полиморфизмом.
Полиморфизм - это способность объекта появляться в нескольких формах. Это включает в себя использование наследования и виртуальных функций для создания семейства объектов, которые можно взаимозаменять. Базовый класс содержит прототипы виртуальных функций, возможно, не реализованные или с реализациями по умолчанию, как диктует приложение, и каждый из производных классов реализует их по-разному, чтобы влиять на различные варианты поведения.
Перегрузка - это когда вы определяете 2 метода с одинаковым именем, но разными параметрами
Переопределение - это когда вы изменяете поведение базового класса с помощью функции с тем же именем в подклассе.
Таким образом, полиморфизм связан с переопределением, но не с перегрузкой.
Однако, если бы кто-то дал мне простой ответ "переопределения" на вопрос "Что такое полиморфизм?" Я бы попросил дальнейших объяснений.
что такое полиморфизм?
Из Java- учебник
Словарное определение полиморфизма относится к принципу в биологии, в котором организм или вид может иметь много различных форм или стадий. Этот принцип также может быть применен к объектно-ориентированному программированию и таким языкам, как язык Java. Подклассы класса могут определять свое собственное уникальное поведение и, тем не менее, совместно использовать некоторые из тех же функций родительского класса.
Рассматривая примеры и определения, следует переопределить ответ.
По поводу вашего второго запроса:
Если у вас есть абстрактный базовый класс, который определил метод без реализации, и вы определили этот метод в подклассе, он все еще переопределяется?
Это следует назвать переопределением.
Посмотрите на этот пример, чтобы понять различные типы переопределения.
- Базовый класс не обеспечивает реализацию, а подкласс должен переопределять полный метод - (аннотация)
- Базовый класс обеспечивает реализацию по умолчанию, а подкласс может изменить поведение
- Подкласс добавляет расширение к реализации базового класса, вызывая
super.methodName()
как первое утверждение - Базовый класс определяет структуру алгоритма (метод Template), а подкласс переопределяет часть алгоритма
фрагмент кода:
import java.util.HashMap;
abstract class Game implements Runnable{
protected boolean runGame = true;
protected Player player1 = null;
protected Player player2 = null;
protected Player currentPlayer = null;
public Game(){
player1 = new Player("Player 1");
player2 = new Player("Player 2");
currentPlayer = player1;
initializeGame();
}
/* Type 1: Let subclass define own implementation. Base class defines abstract method to force
sub-classes to define implementation
*/
protected abstract void initializeGame();
/* Type 2: Sub-class can change the behaviour. If not, base class behaviour is applicable */
protected void logTimeBetweenMoves(Player player){
System.out.println("Base class: Move Duration: player.PlayerActTime - player.MoveShownTime");
}
/* Type 3: Base class provides implementation. Sub-class can enhance base class implementation by calling
super.methodName() in first line of the child class method and specific implementation later */
protected void logGameStatistics(){
System.out.println("Base class: logGameStatistics:");
}
/* Type 4: Template method: Structure of base class can't be changed but sub-class can some part of behaviour */
protected void runGame() throws Exception{
System.out.println("Base class: Defining the flow for Game:");
while ( runGame) {
/*
1. Set current player
2. Get Player Move
*/
validatePlayerMove(currentPlayer);
logTimeBetweenMoves(currentPlayer);
Thread.sleep(500);
setNextPlayer();
}
logGameStatistics();
}
/* sub-part of the template method, which define child class behaviour */
protected abstract void validatePlayerMove(Player p);
protected void setRunGame(boolean status){
this.runGame = status;
}
public void setCurrentPlayer(Player p){
this.currentPlayer = p;
}
public void setNextPlayer(){
if ( currentPlayer == player1) {
currentPlayer = player2;
}else{
currentPlayer = player1;
}
}
public void run(){
try{
runGame();
}catch(Exception err){
err.printStackTrace();
}
}
}
class Player{
String name;
Player(String name){
this.name = name;
}
public String getName(){
return name;
}
}
/* Concrete Game implementation */
class Chess extends Game{
public Chess(){
super();
}
public void initializeGame(){
System.out.println("Child class: Initialized Chess game");
}
protected void validatePlayerMove(Player p){
System.out.println("Child class: Validate Chess move:"+p.getName());
}
protected void logGameStatistics(){
super.logGameStatistics();
System.out.println("Child class: Add Chess specific logGameStatistics:");
}
}
class TicTacToe extends Game{
public TicTacToe(){
super();
}
public void initializeGame(){
System.out.println("Child class: Initialized TicTacToe game");
}
protected void validatePlayerMove(Player p){
System.out.println("Child class: Validate TicTacToe move:"+p.getName());
}
}
public class Polymorphism{
public static void main(String args[]){
try{
Game game = new Chess();
Thread t1 = new Thread(game);
t1.start();
Thread.sleep(1000);
game.setRunGame(false);
Thread.sleep(1000);
game = new TicTacToe();
Thread t2 = new Thread(game);
t2.start();
Thread.sleep(1000);
game.setRunGame(false);
}catch(Exception err){
err.printStackTrace();
}
}
}
выход:
Child class: Initialized Chess game
Base class: Defining the flow for Game:
Child class: Validate Chess move:Player 1
Base class: Move Duration: player.PlayerActTime - player.MoveShownTime
Child class: Validate Chess move:Player 2
Base class: Move Duration: player.PlayerActTime - player.MoveShownTime
Base class: logGameStatistics:
Child class: Add Chess specific logGameStatistics:
Child class: Initialized TicTacToe game
Base class: Defining the flow for Game:
Child class: Validate TicTacToe move:Player 1
Base class: Move Duration: player.PlayerActTime - player.MoveShownTime
Child class: Validate TicTacToe move:Player 2
Base class: Move Duration: player.PlayerActTime - player.MoveShownTime
Base class: logGameStatistics:
Ни:
Перегрузка - это когда у вас одно и то же имя функции, которое принимает разные параметры.
Переопределение - это когда дочерний класс заменяет метод родителя своим собственным (это само по себе не означает полиморфизм).
Полиморфизм является поздним связыванием, например, методы базового класса (родительского) вызываются, но не раньше, чем во время выполнения приложение узнает, что является реальным объектом - это может быть дочерний класс, методы которого отличаются. Это потому, что любой дочерний класс может использоваться там, где определен базовый класс.
В Java вы часто видите полиморфизм с библиотекой коллекций:
int countStuff(List stuff) {
return stuff.size();
}
List - это базовый класс, компилятор не имеет понятия, подсчитывает ли он связанный список, вектор, массив или пользовательскую реализацию списка, если он действует как List:
List myStuff = new MyTotallyAwesomeList();
int result = countStuff(myStuff);
Если бы вы были перегружены, вы бы получили:
int countStuff(LinkedList stuff) {...}
int countStuff(ArrayList stuff) {...}
int countStuff(MyTotallyAwesomeList stuff) {...}
etc...
и верная версия countStuff() будет выбрана компилятором для соответствия параметрам.
Термин "перегрузка" означает наличие нескольких версий одного и того же имени, обычно это методы с разными списками параметров.
public int DoSomething(int objectId) { ... }
public int DoSomething(string objectName) { ... }
Таким образом, эти функции могут делать то же самое, но у вас есть возможность вызвать его с помощью идентификатора или имени. Не имеет ничего общего с наследованием, абстрактными классами и т. Д.
Переопределение обычно относится к полиморфизму, как вы описали в своем вопросе
Я думаю, ребята, вы смешиваете понятия. Полиморфизм - это способность объекта вести себя по-разному во время выполнения. Для этого вам понадобятся два реквизита:
- Позднее связывание
- Наследование.
Сказав, что перегрузка означает что-то отличное от переопределения в зависимости от языка, который вы используете. Например, в Java не существует переопределения, но перегрузки. Перегруженные методы с другой сигнатурой в своем базовом классе доступны в подклассе. В противном случае они будут переопределены (пожалуйста, обратите внимание, что я имею в виду тот факт, что нет способа вызвать метод базового класса извне объекта).
Однако в C++ это не так. Любой перегруженный метод, независимо от того, является ли подпись одинаковой или нет (разная величина, другой тип), также переопределяется. То есть сегодня метод базового класса больше не доступен в подклассе, когда он вызывается извне объекта подкласса, очевидно.
Таким образом, ответ на вопрос о перегрузке использования Java. В любом другом языке может отличаться, как это происходит в C++
Переопределение больше похоже на сокрытие унаследованного метода, объявляя метод с тем же именем и сигнатурой, что и метод верхнего уровня (супер метод), это добавляет полиморфное поведение к классу. другими словами, решение о выборе метода уровня, который будет вызываться, будет приниматься во время выполнения, а не во время компиляции. это приводит к концепции интерфейса и реализации.
Полиморфизм - это множественные реализации объекта, или вы можете сказать несколько форм объекта. допустим, у вас есть класс Animals
как абстрактный базовый класс, и у него есть метод, называемый movement()
который определяет то, как животное движется. Теперь в действительности у нас есть разные виды животных, и они двигаются по-разному, некоторые из них с двумя ногами, другие с четырьмя, а некоторые без ног и т. Д. movement()
каждого животного на земле, мы должны применить полиморфизм. Тем не менее, вам нужно определить больше классов, т.е. Dogs
Cats
Fish
и т.д. Затем вам нужно расширить эти классы из базового класса. Animals
и переопределить его метод movement()
с новой функциональностью движения, основанной на каждом животном, которое у вас есть. Вы также можете использовать Interfaces
чтобы достичь этого. Ключевое слово здесь переопределено, перегрузка отличается и не рассматривается как полиморфизм. с перегрузкой вы можете определить несколько методов "с одинаковым именем", но с разными параметрами для одного и того же объекта или класса.
Переопределение, когда мы наследуем базовый класс и создаем производный класс, тогда, если в производном классе есть метод, имя которого совпадает с именем метода (то же имя, тот же аргумент, тот же тип возвращаемого значения), который определен в базовом классе, то его называют Overriding..
class Vehicle{
void run()
{
System.out.println("Vehicle is running");
}
}
class Bike2 extends Vehicle{
void run()
{
System.out.println("Bike is running safely");
}
public static void main(String args[]){
Bike2 obj = new Bike2();
obj.run();
}
Вывод: велосипед работает безопасно........ Чтобы лучше понять переопределение, посетите: http://javabyroopam.blogspot.in/
Перегрузка Просто два метода, которые имеют одно и то же имя, но имеют другой список аргументов, называются перегрузкой.
class Calculation{
void sum(int a,int b){System.out.println(a+b);}
void sum(int a,int b,int c){System.out.println(a+b+c);}
public static void main(String args[]){
Calculation obj=new Calculation();
obj.sum(10,10,10);
obj.sum(20,20);
}
}
выход 30,20
Полиморфизм является более вероятным, поскольку это означает, что... переопределение в Java
Все дело в разном поведении одного и того же объекта в разных ситуациях (в программировании... вы можете вызывать разные аргументы)
Я думаю, что пример ниже поможет вам понять... Хотя это не чистый код Java...
public void See(Friend)
{
System.out.println("Talk");
}
Но если мы изменим АРГУМЕНТ... ПОВЕДЕНИЕ будет изменено...
public void See(Enemy)
{
System.out.println("Run");
}
Человек (здесь "Объект") такой же...
import java.io.IOException;
class Super {
protected Super getClassName(Super s) throws IOException {
System.out.println(this.getClass().getSimpleName() + " - I'm parent");
return null;
}
}
class SubOne extends Super {
@Override
protected Super getClassName(Super s) {
System.out.println(this.getClass().getSimpleName() + " - I'm Perfect Overriding");
return null;
}
}
class SubTwo extends Super {
@Override
protected Super getClassName(Super s) throws NullPointerException {
System.out.println(this.getClass().getSimpleName() + " - I'm Overriding and Throwing Runtime Exception");
return null;
}
}
class SubThree extends Super {
@Override
protected SubThree getClassName(Super s) {
System.out.println(this.getClass().getSimpleName()+ " - I'm Overriding and Returning SubClass Type");
return null;
}
}
class SubFour extends Super {
@Override
protected Super getClassName(Super s) throws IOException {
System.out.println(this.getClass().getSimpleName()+ " - I'm Overriding and Throwing Narrower Exception ");
return null;
}
}
class SubFive extends Super {
@Override
public Super getClassName(Super s) {
System.out.println(this.getClass().getSimpleName()+ " - I'm Overriding and have broader Access ");
return null;
}
}
class SubSix extends Super {
public Super getClassName(Super s, String ol) {
System.out.println(this.getClass().getSimpleName()+ " - I'm Perfect Overloading ");
return null;
}
}
class SubSeven extends Super {
public Super getClassName(SubSeven s) {
System.out.println(this.getClass().getSimpleName()+ " - I'm Perfect Overloading because Method signature (Argument) changed.");
return null;
}
}
public class Test{
public static void main(String[] args) throws Exception {
System.out.println("Overriding\n");
Super s1 = new SubOne(); s1.getClassName(null);
Super s2 = new SubTwo(); s2.getClassName(null);
Super s3 = new SubThree(); s3.getClassName(null);
Super s4 = new SubFour(); s4.getClassName(null);
Super s5 = new SubFive(); s5.getClassName(null);
System.out.println("Overloading\n");
SubSix s6 = new SubSix(); s6.getClassName(null, null);
s6 = new SubSix(); s6.getClassName(null);
SubSeven s7 = new SubSeven(); s7.getClassName(s7);
s7 = new SubSeven(); s7.getClassName(new Super());
}
}
Полиморфизм относится к способности языка одинаково обрабатывать различные объекты с использованием единого интерфейса; как таковой он связан с переопределением, поэтому интерфейс (или базовый класс) является полиморфным, а разработчик - это объект, который переопределяет (две грани одной медали)
в любом случае, разницу между этими двумя терминами лучше объяснить с помощью других языков, таких как C++: полиморфный объект в C++ ведет себя как аналог java, если базовая функция виртуальная, но если метод не виртуальный, переход кода разрешается статически, и истинный тип не проверяется во время выполнения, поэтому полиморфизм включает способность объекта вести себя по-разному в зависимости от интерфейса, используемого для доступа к нему; позвольте мне привести пример в псевдокоде:
class animal {
public void makeRumor(){
print("thump");
}
}
class dog extends animal {
public void makeRumor(){
print("woff");
}
}
animal a = new dog();
dog b = new dog();
a.makeRumor() -> prints thump
b.makeRumor() -> prints woff
(предположим, что makeRumor НЕ является виртуальным)
Java действительно не предлагает этот уровень полиморфизма (называемый также разрезанием объектов).
животное а = новая собака (); собака б = новая собака ();
a.makeRumor() -> prints thump
b.makeRumor() -> prints woff
в обоих случаях он будет печатать только woff.. так как a и b ссылаются на классную собаку
Полиморфизм
Полиморфизм в Java — это концепция, согласно которой мы можем выполнять одно и то же действие разными способами. Полиморфизм происходит от двух греческих слов: поли и морфы. Слово «поли» означает множество, а «морфы» — формы. Итак, полиморфизм означает множество форм.
В Java есть два типа полиморфизма: полиморфизм времени компиляции и полиморфизм времени выполнения. Мы можем реализовать полиморфизм в java путем перегрузки и переопределения методов.
мы также можем назвать это статической привязкой (перегрузка) и динамической привязкой (переопределение)
Переопределение (полиморфизм во время выполнения)
Полиморфизм среды выполнения , или диспетчеризация динамических методов , или динамическое связывание — это процесс, в котором вызов переопределенного метода разрешается во время выполнения, а не во время компиляции.
В этом процессе переопределенный метод вызывается через ссылочную переменную суперкласса. Определение вызываемого метода основано на объекте, на который ссылается ссылочная переменная.
Пример полиморфизма во время выполнения
В этом примере мы создаем два класса MotorCycle и CBR150R. Класс Splendor расширяет класс MotorCycle и переопределяет его метод run(). Мы вызываем метод run по ссылочной переменной класса Parent. Поскольку он ссылается на объект подкласса, а метод подкласса переопределяет метод родительского класса, метод подкласса вызывается во время выполнения.
Поскольку вызов метода определяется JVM, а не компилятором, он известен как полиморфизм времени выполнения.
class MotorCycle{
void run(){
System.out.println("running");
}
}
class CBR150R extends Bike{
void run(){
System.out.println("running safely with 80km");
}
public static void main(String args[]){
Bike b = new Splendor();//upcasting
b.run();
}
}
Выход:
running safely with 80km.
Перегрузка (полиморфизм времени компиляции)
Если класс имеет несколько методов с одинаковыми именами, но разными параметрами, это называется перегрузкой методов.
Если нам нужно выполнить только одну операцию, одинаковые имена методов повышают читабельность программы.
Предположим, вам нужно выполнить сложение заданных чисел, но может быть любое количество аргументов, если вы напишете метод, такой как a(int,int) для двух параметров, и b(int,int,int) для трех параметров, тогда он Вам, как и другим программистам, может быть сложно понять поведение метода, потому что его имя отличается.
Итак, мы выполняем перегрузку методов, чтобы быстро разобраться в программе.
Преимущество перегрузки метода
Перегрузка методов повышает читабельность программы.
В этом примере мы создали два метода, первый метод add() выполняет сложение двух чисел, а второй метод add выполняет сложение трех чисел.
В этом примере мы создаем статические методы, поэтому нам не нужно создавать экземпляр для вызова методов.
class Adder{
static int add(int a,int b){return a+b;}
static int add(int a,int b,int c){return a+b+c;}
}
class TestOverloading1{
public static void main(String[] args){
System.out.println(Adder.add(11,11));
System.out.println(Adder.add(11,11,11));
}
}
выход:
22
33
Полиморфизм против перегрузки против переопределения
Перегрузка — это действие по определению нескольких методов с одним и тем же именем, но с разными параметрами.
Переопределение означает переопределение тела метода суперкласса в подклассе для изменения поведения метода.
Какая объектно-ориентированная концепция достигается с помощью перегрузки и переопределения? Полиморфизм может быть достигнут путем переопределения . Как уже упоминалось выше, полиморфизм относится к способности объекта обеспечивать различное поведение (использовать разные реализации) в зависимости от его собственной природы. В частности, в зависимости от его положения в иерархии классов.
Перегрузка методов не связана с полиморфизмом. Как упоминалось выше, это относится к определению различных форм метода. Это можно рассматривать как статический полиморфизм. Решение о вызове той или иной реализации принимается во время кодирования. Обратите внимание, что в этом случае сигнатура метода должна измениться.