Можем ли мы избежать наследования без использования ключевого слова final? - PullRequest
0 голосов
/ 11 сентября 2018

Я просто хочу знать, что я могу иметь 2 класса A и B. Я не хочу, чтобы класс B расширял класс A. Какой метод я должен применить в классе A, чтобы класс B не мог наследовать класс A.Я не хочу делать класс А финальным.Любое другое решение вместо того, чтобы сделать класс А финальным?

Ответы [ 7 ]

0 голосов
/ 11 сентября 2018

Фактически, практика, которой я стараюсь следовать и которую рекомендует Джош Блох в своей книге «Эффективная Java», является в точности обратным правилом того, что вам говорили: если вы не задумывались о наследовании, разработайте свой класс чтобы быть унаследованным, и задокументировать, как должен наследоваться ваш класс, вы всегда должны отключать наследование.

Я бы рекомендовал прочитать эту главу «Эффективная Java» (вы не пожалеете о ее покупке) и показать ее человеку, который рассказал вам об этом правиле.

Самая очевидная причина запрета наследования - неизменность. Неизменяемый объект прост в использовании (только одно состояние), может кэшироваться, совместно использоваться многими объектами и по своей сути поточно-ориентирован. Если класс наследуется, любой может расширить класс и сделать его изменяемым, добавив изменяемые атрибуты. https://stackoverflow.com/a/10464466/5010396

0 голосов
/ 11 сентября 2018

Вы можете выполнить одно из следующих действий, чтобы избежать наследования без использования ключевого слова final:

  • Использовать приватный конструктор (ы).
  • Отметьте каждый метод final, чтобы дети не могли их переопределить.
  • Бросьте исключение времени выполнения в конструкторе, если хотите ограничить наследование для некоторых нежелательных детей (хотя v.rude), например,
    if (this.getClass() != FavoriteChild.class) throw new RuntimeException("Not Allowed")
0 голосов
/ 11 сентября 2018

У вас есть возможность ограничить конструктор следующим образом.

if (this.getClass() != MyClass.class) {
    throw new RuntimeException("Subclasses not allowed");
}

Для более подробной информации проверьте сообщение.

https://stackoverflow.com/a/451229/6742601

0 голосов
/ 11 сентября 2018

На самом деле вы можете, поместив класс A в пакет без модификатора для класса (package modifier).

package com.example.package1;

// Class A with package modifier.
class A {

}

И положить класс B в другой пакет

package com.example.package2;

// Class B cannot extends from class A, compiler error.
public class B extends A {

}

Ограничение: только классы в одном пакете могут выходить из класса A.

0 голосов
/ 11 сентября 2018

Вы можете пометить конструктор класса A как закрытый.:)

PS: Если вы также хотите избежать атак отражением, выведите некоторую ошибку из конструктора и отметьте ее как частную.

0 голосов
/ 11 сентября 2018

Я не понимаю конкретный вариант использования вашего требования, но это может сработать.

Если вы готовы вносить изменения в B

ВКаждый конструктор B проверяет, является ли он экземпляром A, а затем выдает ошибку.

if(A.class.isAssignableFrom(B.class)) {
        System.out.println(true);
        throw new IllegalStateException();
    }
    else
        System.out.println(false);
0 голосов
/ 11 сентября 2018

Это невозможно "хорошими способами".Язык Java позволяет вам либо иметь ключевое слово final в вашем определении класса, либо не иметь его.

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

В случае, если вам нужно создать экземпляр Aу вас все еще может быть фабричный метод, например:

public class A {
  private A() { ... }
  private A(String foo) { ... }

  public static A newInstance(String foo) { return new A(foo); }

.

Но имейте в виду: код написан для ваших читателей-людей.Если ваше намерение должно иметь окончательный класс, тогда правильный ответ - использовать это ключевое слово final.

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

...