Статический полиморфизм в java

Полиморфизм в Java

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

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

2. Статический полиморфизм

Согласно Википедии , статический полиморфизм — это имитация полиморфизма, которая разрешается во время компиляции и, таким образом, устраняет поиск виртуальных таблиц во время выполнения .

Например, наш класс TextFile в приложении файлового менеджера может иметь три метода с одинаковой сигнатурой метода read() :

 public class TextFile extends GenericFile    //.    public String read()    return this.getContent()   .toString();   >    public String read(int limit)    return this.getContent()   .toString()   .substring(0, limit);   >    public String read(int start, int stop)    return this.getContent()   .toString()   .substring(start, stop);   >   > 

Во время компиляции кода компилятор проверяет, что все вызовы метода чтения соответствуют по крайней мере одному из трех методов, определенных выше.

3. Динамический полиморфизм

При динамическом полиморфизме виртуальная машина Java (JVM) обрабатывает обнаружение соответствующего метода для выполнения, когда подкласс назначается его родительской форме . Это необходимо, поскольку подкласс может переопределить некоторые или все методы, определенные в родительском классе.

В гипотетическом приложении файлового менеджера давайте определим родительский класс для всех файлов с именем GenericFile :

 public class GenericFile    private String name;    //.    public String getFileInfo()    return "Generic File Impl";   >   > 

Мы также можем реализовать класс ImageFile , который расширяет GenericFile, но переопределяет метод getFileInfo() и добавляет дополнительную информацию:

 public class ImageFile extends GenericFile    private int height;   private int width;    //. getters and setters    public String getFileInfo()    return "Image File Impl";   >   > 

Когда мы создаем экземпляр ImageFile и назначаем его классу GenericFile , выполняется неявное приведение типов. Однако JVM сохраняет ссылку на фактическую форму ImageFile .

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

 public static void main(String[] args)    GenericFile genericFile = new ImageFile("SampleImageFile", 200, 100,   new BufferedImage(100, 200, BufferedImage.TYPE_INT_RGB)   .toString()   .getBytes(), "v1.0.0");   logger.info("File Info: \n" + genericFile.getFileInfo());   > 

Как и ожидалось, genericFile.getFileInfo() запускает метод getFileInfo () класса ImageFile , как показано в выводе ниже:

 File Info:  Image File Impl 

4. Другие полиморфные характеристики в Java

Помимо этих двух основных типов полиморфизма в Java, в языке программирования Java есть и другие характеристики, демонстрирующие полиморфизм. Давайте обсудим некоторые из этих характеристик.

4.1. Принуждение

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

4.2. Перегрузка оператора

Перегрузка оператора или метода относится к полиморфной характеристике одного и того же символа или оператора, имеющего разные значения (формы) в зависимости от контекста.

Например, символ плюса (+) можно использовать для математического сложения, а также для конкатенации строк . В любом случае только контекст (т.е. типы аргументов) определяет интерпретацию символа:

 String str = "2" + 2;   int sum = 2 + 2;   System.out.printf(" str = %s\n sum = %d\n", str, sum); 

4.3. Полиморфные параметры

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

 public class TextFile extends GenericFile    private String content;    public String setContentDelimiter()    int content = 100;   this.content = this.content + content;   >   > 

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

Чтобы решить эту проблему, часто рекомендуется использовать глобальные ссылки, такие как ключевое слово this , чтобы указывать на глобальные переменные в локальном контексте.

4.4. Полиморфные подтипы

Полиморфный подтип удобно позволяет нам назначать несколько подтипов типу и ожидать, что все вызовы типа вызовут доступные определения в подтипе.

Например, если у нас есть коллекция GenericFile и мы вызываем метод getInfo() для каждого из них, мы можем ожидать, что вывод будет отличаться в зависимости от подтипа, из которого был получен каждый элемент в коллекции:

 GenericFile [] files = new ImageFile("SampleImageFile", 200, 100,   new BufferedImage(100, 200, BufferedImage.TYPE_INT_RGB).toString()   .getBytes(), "v1.0.0"), new TextFile("SampleTextFile",   "This is a sample text content", "v1.0.0")>;    for (int i = 0; i  files.length; i++)    files[i].getInfo();   > 

Полиморфизм подтипа стал возможным благодаря сочетанию повышения приведения и позднего связывания . Upcasting включает приведение иерархии наследования от супертипа к подтипу:

 ImageFile imageFile = new ImageFile();   GenericFile file = imageFile; 

Результатом вышеизложенного является то, что методы, специфичные для ImageFile , не могут быть вызваны для нового восходящего GenericFile . Однако методы в подтипе переопределяют аналогичные методы, определенные в супертипе.

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

 ImageFile imageFile = (ImageFile) file; 

Стратегия позднего связывания помогает компилятору решить, чей метод активировать после преобразования . В случае i mageFile#getInfo vs file#getInfo в приведенном выше примере компилятор сохраняет ссылку на метод getInfo ImageFile . «

5. Проблемы с полиморфизмом

Давайте рассмотрим некоторые неясности в полиморфизме, которые потенциально могут привести к ошибкам во время выполнения, если их не проверить должным образом.

5.1. Идентификация типа во время преобразования вниз

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

Например, если мы выполняем приведение вверх и последующее приведение вниз:

 GenericFile file = new GenericFile();   ImageFile imageFile = (ImageFile) file;   System.out.println(imageFile.getHeight()); 

Мы заметили, что компилятор допускает преобразование GenericFile в ImageFile , даже несмотря на то, что класс на самом деле является GenericFile , а не ImageFile .

Следовательно, если мы попытаемся вызвать метод getHeight() для класса imageFile , мы получим исключение ClassCastException , поскольку GenericFile не определяет метод getHeight() :

 Exception in thread "main" java.lang.ClassCastException: GenericFile cannot be cast to ImageFile 

Чтобы решить эту проблему, JVM выполняет проверку информации о типе во время выполнения (RTTI). Мы также можем попытаться явно определить тип, используя ключевое слово instanceof , например:

 ImageFile imageFile;   if (file instanceof ImageFile)    imageFile = file;   > 

Вышеизложенное помогает избежать исключения ClassCastException во время выполнения. Другой вариант, который можно использовать, — это обернуть приведение в блок try and catch и перехватить ClassCastException.

Следует отметить, что проверка RTTI является дорогостоящей из-за времени и ресурсов, необходимых для эффективной проверки правильности типа. Кроме того, частое использование ключевого слова instanceof почти всегда подразумевает плохой дизайн.

5.2. Проблема хрупкого базового класса

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

Давайте рассмотрим объявление суперкласса GenericFile и его подкласса TextFile :

 public class GenericFile    private String content;    void writeContent(String content)    this.content = content;   >   void toString(String str)    str.toString();   >   > 
 public class TextFile extends GenericFile    @Override   void writeContent(String content)    toString(content);   >   > 

Когда мы модифицируем класс GenericFile :

 public class GenericFile    //.    void toString(String str)    writeContent(str);   >   > 

Мы видим, что приведенная выше модификация оставляет TextFile в бесконечной рекурсии в методе writeContent() , что в конечном итоге приводит к переполнению стека.

Чтобы решить проблему хрупкого базового класса, мы можем использовать ключевое слово final , чтобы предотвратить переопределение метода writeContent() подклассами . Надлежащая документация также может помочь. И последнее, но не менее важное: композицию обычно следует предпочитать наследованию.

6. Заключение

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

Как всегда, исходный код этой статьи доступен на GitHub .

Источник

Статический полиморфизм в java

Формула: площадь треугольника: ½ * основания * высоту Формула: площадь круга: 3,14 * радиус * радиус 

🧩☕ Интересные задачи по Java для практики можно найти на нашем телеграм-канале «Библиотека задач по Java»

Типы полиморфизма

Два варианта реализации полиморфизма в Java:

При перегрузке метода мы в одном классе создаем нескольких методов с одинаковым названием, но разным функционалом, например:

class Forms < public void shapearea()) < System.out.println("Площади фигур:"); >public void shapearea(int r) < System.out.println("Sкруга = "+3.14*r*r); >public void shapearea(double b, double h) < System.out.println("Sтреугольника ="+0.5*b*h); >public void shapearea(int l, int b) < System.out.println("Sпрямоугольника ="+l*b); >> class Main < public static void main(String[] args) < Forms xForms = new Forms(); xForms.shapearea(); xForms.shapearea(3); xForms.shapearea(2.5, 2.0); xForms.shapearea(4,7); >> 
Площади фигур: Sкруга = 28.26 Sтреугольника = 5 Sпрямоугольника = 28 

При п ереопределении название метода дочернего класса мы ставим такое же, как и уже объявленного метода родительского класса, например:

class Vehicles < // определение метода void drive()< System.out.println("Управлять транспортным средством");>> //создаем дочерний класс class Cars extends Vehicles < //определяем одноименный метод void drive()< System.out.println("Управлять автомобилем"); >public static void main(String args[]) < Cars model= new Cars(); //создание нового объекта model.drive(); //вызов переопределенного метода >> 

Статический и динамический полиморфизм

При статическом варианте развития событий метод можно вызывать при компиляции кода, происходит это при помощи рассмотренной нами ранее концепции — перегрузки методов. Говоря проще, у них будет одинаковое название, но разная функциональная начинка (тип возвращаемых данных, свойств и т. д.). В таком случае Java позволяет пользователю свободно определять функциям идентичные названия, если они будут отличны по типу и параметрам, например:

public class Add < void sum(int x, int y) < int z = x + y; System.out.println(“Сумма двух чисел: ”+z); >void sum(int x, int y, int i) < int z = x + y + i; System.out.println(“Сумма трех чисел: ”+z); >public static void main(String[] args) < Add myAdd = new Add(); myAdd.sum(5, 11); myAdd.sum(1, 8, 11); >> 
Сумма двух чисел: 16 Сумма трех чисел: 20 

При динамическом полиморфизме вызвать переопределеный метод можно при выполнении программы. Скопировать метод родительского класса и обозначить его в дочернем можно с помощью ссылочной переменной родительского класса, при этом вызванный метод будет определяться по объекту, на который она ссылается. Такую операцию еще называют Upcasting, например:

Создадим один родительский класс Звери и три подкласса: травоядные , плотоядные и всеядные . В этом случае классы-потомки расширили родителя и переопределили его метод eat() .

class Beast < void eat() < System.out.println("Животные питаются:"); >> class herbivorous extends Beast < void eat() < System.out.println("Травоядные едят растения"); >> class omnivorous extends Beast < void eat() < System.out.println("Всеядные едят растения и мясо"); >> class carnivorous extends Beast < void eat() < System.out.println("Хищники едят только мясо"); >> class main < public static void main(String args[]) < Beast X = new Beast(); Beast herb = new herbivorous(); Beast omni = new omnivorous(); Beast carn = new carnivorous(); X.eat(); herb.eat(); omni.eat(); carn.eat(); >> 
Животные питаются: Травоядные едят растения Всеядные едят растения и мясо Хищники едят мясо 

Преимущества и недостатки полиморфизма

  1. Предоставляет возможность повторного использования кода. Реализованные классы можно многократно использовать повторно. Кроме того, это экономит много времени разработчику, ведь при этом появляется возможность поменять что-то в программе, не трогая исходный код.
  2. Одна переменная может использоваться для хранения нескольких значений данных. Значение переменной в дочернем классе, наследуемой от родительского, может быть изменено без изменения родительской переменной.
  3. Легче отлаживать код, когда его не так много.

Помимо преимуществ, у рассматриваемой парадигмы есть еще и несколько недостатков:

  1. Полиморфизм не так просто реализовать на деле.
  2. Такой подход снижает читаемость кода.
  3. Может вызывать серьезные проблемы с производительностью в режиме реального времени.

Мы надеемся, что у вас сложилось правильное общее представление о полиморфизме в Java и о том, как его использовать. С более подробной информации по теме можно ознакомиться на официальном сайте Oracle .

Материалы по теме

Источник

Читайте также:  Start a css file
Оцените статью