Инициализация объекта - отношение конструктора к типу класса и доступу к методу - PullRequest
2 голосов
/ 28 мая 2019

У меня есть следующий код:

package testapp;

class Calculation {

    public Calculation(){}

    public void addition(int x, int y) {   }

    public void Subtraction(int x, int y) {   }
}

class My_Calculation extends Calculation {

    public My_Calculation(){}

    public void multiplication(int x, int y) {   }
}

public class TestApp {

    public static void main(String[] args) {

        int a = 20, b = 10;

        My_Calculation demo = new My_Calculation();
        demo.addition(a, b);
        demo.Subtraction(a, b);
        demo.multiplication(a, b);
        System.out.println(demo.getClass().getName());

        Calculation d = new Calculation();
        d.Subtraction(b, b);
        d.addition(b, b);
        // no multiplication
        System.out.println(d.getClass().getName());

        Calculation d2 = new My_Calculation();
        d2.Subtraction(b, b);
        d2.addition(b, b);
        // no multiplication
        System.out.println(d2.getClass().getName());
    } 
}

Вывод такой:

demo = testapp.My_Calculation

d = testapp. Расчет

d2 = testapp.My_Calculation

Следующий оператор является объявлением, в котором имя переменной d2 ссылается на тип объекта Расчет :

Calculation d2;

Следующий оператор является инициализацией, которая вызывает конструктор My_Calculation.

Calculation d2 = new My_Calculation();

Когда я запускаю следующий код, вывод сообщает, что d2 имеет тип класса My_Calculation , но дает доступ к методам в Класс вычисления .

System.out.println(d2.getClass().getName());

Вывод: testapp.My_Calculation

Доступ: дополнение; Вычитание

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

1. ТОЛЬКО доступ к умножению, или

2. доступ к сложению, вычитанию и умножению.

Но на самом деле я получаю обратное: ТОЛЬКО доступ к сложению и вычитанию . Таким образом, я считаю это нелогичным.

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

Ответы [ 3 ]

2 голосов
/ 28 мая 2019

Может ли кто-нибудь объяснить мне, что здесь происходит, чтобы дать мне последовательное понимание того, почему объект типа: My_Calculation будет не имеют доступа к своим собственным методам, но имеют доступ только к суперклассам методы.

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

Calculation d2 = new My_Calculation();

Или ссылка на объект, если вы не объявляете переменную, такую ​​как:

new My_Calculation().doMethod();

Компилятор рассматривает d2 как Calculation и поэтому позволяет делать то, что предлагает этот класс.
Этот способ называется программированием по интерфейсу / супертипу. Это означает, что вы можете передать любую реализацию в созданном типе, и код всегда будет работать.
Например, с этим изменением:

Calculation d2 = new MyCalculationOtherSubclass();

Весь код может скомпилироваться, если MyCalculationOtherSubclass является другим подклассом Calculation.

Если вам нужно работать с определенным подтипом, объявление подтипа имеет смысл:

My_Calculation d2 = new My_Calculation();

или около того, опустив его: Точно так же String является подклассом CharSequence, но если вы хотите использовать subString(), специфичный для String, вам нужно манипулировать String: Таким образом, вы объявите это: String aString = "...";, а не CharSequence aString = "...";.

2 голосов
/ 28 мая 2019

Здесь:

Calculation d2 = new My_Calculation();

Вы создаете объект типа My_Calculation, но вы назначаете его переменной, объявленной с супертипом!И компилятор не достаточно умен, чтобы «увидеть», что d2 на самом деле дочернего типа.Таким образом, компилятор не позволяет вам вызывать метод, определенный для этого дочернего типа.Поскольку компилятору, d2, как известно, Calculation!

Обратите внимание, что вы можете cast сказать компилятору "Я знаю лучше":

( (My_Calculation) d2) ).multiplication(...

, который будет работать во время компиляции, а также во время выполнения!

Ваше заблуждение начинается здесь:

Calculation d2 = new My_Calculation();

То, что вы делаете в правой части задания, в основном "забыто" в следующей строке.Тогда компилятор только знает, что у вас есть d2 типа Calculation!

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

1 голос
/ 28 мая 2019

Это потому, что во время компиляции у вас есть доступ только к методам из статического типа объекта, с которым вы имеете дело (это тип вашей переменной), и к методам из его суперклассов, интерфейсов и суперинтерфейсов. Метод getClass доступен, потому что он определен внутри класса Object, а Calculation наследуется от него. Следовательно, если вы хотите вызвать multiplication, вы должны объявить переменную как My_Calculation.

Однако во время выполнения вызывается метод динамического типа (тот, который вы назначаете переменной). Таким образом, если вы переопределите метод addition внутри My_Calculation, он будет вызван во время выполнения, даже если статический тип равен Calculation.

...