Философия Java

         

Внутренний класс и приведение к базовому типу


Недавно, Вы узнали о том, что в Java есть достаточно хорошие механизмы для скрытия классов, их достаточно сделать "friendly" и они будут видны только для классов этого же пакета, и не нужно никаких внутренних классов.

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

Сперва общие интерфейсы должны быть определены в их собственных файлах, тогда они могут быть использованы во всех примерах:

//: c08:Destination.java

public interface Destination { String readLabel(); } ///:~

//: c08:Contents.java

public interface Contents { int value(); } ///:~

Теперь Contents и Destination представляют интерфейсы доступные для программиста - клиента. (Не забудьте, что interface автоматически делает всех членов класса public.)

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

//: c08:Parcel3.java

// Возвращение ссылки на внутренний класс.

public class Parcel3 { private class PContents implements Contents { private int i = 11; public int value() { return i; } } protected class PDestination implements Destination { private String label; private PDestination(String whereTo) { label = whereTo; } public String readLabel() { return label; } } public Destination dest(String s) { return new PDestination(s); } public Contents cont() { return new PContents(); } }

class Test { public static void main(String[] args) { Parcel3 p = new Parcel3(); Contents c = p.cont(); Destination d = p.dest("Tanzania"); // Незаконно - нельзя получить доступ к private классу:


//! Parcel3.PContents pc = p.new PContents();

} } ///:~

Заметьте, поскольку main( ) в Test, то когда Вы захотите запустить эту программу, Вы не выполните Parcel3, а вместо этого:

java Test

В примере, main( ) должен быть расположен в отдельном классе, для того, что бы продемонстрировать защищенность внутреннего класса PContents.

В Parcel3 было добавлено что-то новое: внутренний класс PContents - private, так что никто кроме Parcel3 не может получить к нему доступ. PDestination является protected, так что никто кроме Parcel3 и классов из пакета Parcel3 (поскольку protected так же дает доступ к членам пакета, protected так же означает "friendly"), и наследников Parcel3 не смогут получить доступ к PDestination. Это означает, что клиентский программист имеет ограниченные знания об этих объектах и ограниченный доступ к ним. В действительности, Вы никогда не сможете привести к дочернему типу private внутренний класс (или к protected внутреннему классу, даже если Вы являетесь наследующим), и это происходит потому, что Вы не можете получить доступ к имени, как это можно посмотреть в классе Test. Поэтому private внутренний класс предоставляет разработчику возможность полностью исключить изменение его кода и полностью скрыть детали его реализации. В дополнение, расширение интерфейса так же не принесет пользы, поскольку клиент программист не сможет получить доступ ни к одному из методов, поскольку они не являются частью public interface класса. При этом так же имеется возможность для компилятора по созданию более эффективного кода.

Нормальный (не внутренний) класс не может быть сделан private или protected, только как public или friendly.


Содержание раздела