Помогите разобраться в проблеме с защищенным методом - PullRequest
9 голосов
/ 11 февраля 2010

Я читаю Руководство по сертификации Sybex Complete Java 2, апрель 2005 г. (ISBN0782144195). Эта книга для разработчиков Java, которые хотят пройти сертификацию Java.

После главы о модификаторах доступа (вместе с другими модификаторами) я нашел следующий вопрос (# 17):

Истина или ложь: если класс Y расширяется класс X, два класса находятся в различные пакеты, и класс X имеет защищенный метод называется abby (), затем любой экземпляр Y может вызывать abby () метод любого другого экземпляра Y.

Этот вопрос меня немного смутил.

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

Например, вы не можете клонировать какой-либо объект только потому, что вы его унаследовали.

Но вопросы ничего не говорят о типе переменной, только о типе экземпляра.

Я немного смутился и ответил "правда".

Ответ в книге

Ложные. Объект, который наследует защищенный метод от суперкласса в другом пакете, может вызывать этот метод сам по себе, но не в других экземплярах того же класса.

Здесь нет ничего о типе переменной, только о типе экземпляра.

Это очень странно, я не понимаю.

Кто-нибудь может объяснить, что здесь происходит?

Ответы [ 6 ]

2 голосов
/ 11 февраля 2010

True или false: если класс Y расширяет класс X, два класса находятся в разных пакетах, а класс X имеет защищенный метод, называемый abby (), то любой экземпляр Y может вызывать метод abby () любого другого экземпляра. из Y.

"False. Объект, который наследует защищенный метод от суперкласса в другом пакете, может вызывать этот метод сам по себе, но не в других экземплярах того же класса".

Давайте запишем это, как BalusC , и добавим к Y метод, который вызывает abby () любого другого экземпляра Y:

package one;
public class X {
    protected void abby() {
    }
}

package other;
import one.X;
public class Y extends X {
    public void callAbbyOf(Y anyOther) {
        anyOther.abby();
    }
}

Для возможно для Y вызвать метод abby () любого экземпляра Y , на который он ссылается . Таким образом, ответ в книге явно неправильный. Java не имеет специфичных для экземпляра областей (в отличие, например, от Scala, которая имеет частную область действия экземпляра).

Если мы попытаемся быть милосердными, возможно, вопрос будет звучать так: « любой другой экземпляр Y», который может получить доступ к методу любого экземпляра Y, который окажется в памяти - что невозможно, поскольку у Java нет прямого доступа к памяти. Но в этом случае вопрос настолько плохо сформулирован, что вы даже можете ответить: «Неверно. Вы не можете вызывать методы для экземпляров, которые находятся на другой JVM, или экземпляров, которые были собраны сборщиком мусора, или экземпляров на JVM, которые умерли год назад и т. д. "

1 голос
/ 11 февраля 2010

С Спецификация языка Java :

6.6.2.1 Доступ к защищенному участнику

Пусть C будет классом, в котором объявлен защищенный член m. Доступ разрешен только в теле подкласса S из C. Кроме того, если Id обозначает поле экземпляра или метод экземпляра, то:

  • Если доступ осуществляется по полному имени Q.Id, где Q - ExpressionName, тогда доступ разрешается тогда и только тогда, когда тип выражения Q - S или подкласс S.
  • Если доступ осуществляется с помощью выражения доступа к полю E.Id, где E - первичное выражение, или с помощью выражения вызова метода E.Id (...), Где E - первичное выражение, то доступ разрешается тогда и только тогда, когда тип E является S или подклассом S.

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

0 голосов
/ 09 марта 2010

Я почти уверен, что вопрос означал:

«любой экземпляр Y может вызвать метод abbey () любого другого экземпляра X » (не Y).

В этом случае он действительно потерпит неудачу. Чтобы заимствовать пример из другого ответа выше, выполните следующее:

package one;
public class X {
    protected void abby() {
    }
}

package other;
import one.X;
public class Y extends X {
    public void callAbbyOf(X anyOther) {
        anyOther.abby();
    }
}

не скомпилируется.

Спецификация языка Java объясняет, почему здесь: http://java.sun.com/docs/books/jls/third_edition/html/names.html#6.6.2

6.6.2.1 Доступ к защищенному участнику

Пусть C - класс, в котором защищенный член m объявлен. Доступ разрешено только в теле подкласс S C. Кроме того, если Id обозначает поле экземпляра или экземпляр метод, то: если доступ осуществляется квалифицированное имя Q.Id, где Q является ExpressionName, то доступ разрешено, если и только если тип выражение Q является S или подклассом S. Если доступ осуществляется через поле доступа выражение E.Id, где E является первичным выражением или вызовом метода выражение E.Id (...), где E представляет собой Основное выражение, затем доступ разрешено, если и только если тип E это S или подкласс S. (выделение мое).

0 голосов
/ 11 февраля 2010

Потому что тип переменной здесь не имеет значения, пока не станет «нормальным» в контексте вопроса. Поскольку метод abby() принадлежит XY наследует его), не имеет значения, с какой переменной типа объявлена ​​ссылка на экземпляр переменной Y: это может быть либо X, либо Y. Будучи abby() доступным, мы можем вызвать его через обе переменные:

X myY1 = new Y();
myY1.abby();

Y myY2 = new Y();
myY2.abby();
0 голосов
/ 11 февраля 2010

Этот вопрос кажется плохо сформулированным и задает вопрос об очень редком крайнем случае (что я даже не уверен, что рассматривается в тесте SCJP).То, как оно сформулировано, делает ваш ответ правильным, а данный - неправильным.Кодирование аналогичной конструкции и ее запуск легко подтверждают это ...

package inside;

public class Base {

    private String name;

    public Base(String name)  {
        this.name = name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    protected String abby(String name) {
        String old = this.name;
        this.name = name;
        return old;
    }
}




package outside;
import inside.Base;

public class Another extends Base {

    public Another(String name) {
        super(name);
    }

    public String setAnother(Another another, String hack) {
        return another.abby(hack);
    }

    public static void doCrazyStuff() {
        Another one = new Another("one");
        Another two = new Another("two");

        one.abby("Hi one"); 
        two.abby("Hi two");
        one.setAnother(two, "Hi two from one");

        System.out.println("one = " + one.getName());
        System.out.println("two = " + two.getName());

    }

    public static void main(String[] args) {
        Another.doCrazyStuff();
    }
}
0 голосов
/ 11 февраля 2010

True или false: если класс Y расширяет класс X, два класса находятся в разных пакетах, а класс X имеет защищенный метод, называемый abby (), то любой экземпляр Y может вызывать метод abby () любого другого экземпляра. из Y.

Давайте изобразим это.

Класс X:

package one;
public class X {
    protected void abby() {}
}

Класс Y:

package other;
public class Y extends X {}

TestCase:

public static void main(String[] args) {
    Y y1 = new Y();
    Y y2 = new Y();
    Y y3 = new Y();
    // ...
}

Теперь перечитайте вопрос: можно ли y1 позвонить abby() на y2, y3 и т. Д.? Будут ли звонить abby() на y1 также звонить на номера y2, y3 и т. Д.?

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...