2014-09-25 2 views
3

Почему возможно вернуть вложенный класс private из public в классе public? Разве компилятор не жалуется на видимость типа возвращаемого типа меньше метода?Как публичный метод возвращает частный тип?

public final class Outer { 
    private static final class Configurator { 
     private Configurator() { 
     } 
    } 

    public static Configurator configure() { 
     return new Configurator(); 
    } 
} 
+2

Это невозможно. Пожалуйста, разместите код. Я предполагаю, что подпись метода _returns_ является общедоступным интерфейсом (или подобным), тогда как реализация метода возвращает частный подкласс этого интерфейса. Это разрешено. – Seelenvirtuose

+1

@Seelenvirtuose: It * is * возможно, удивительно. Похоже на недостаток дизайна Java. (C# не допускает этого.) –

+0

А, ок. Понимаю. Простое техническое объяснение. В рамках этого описания метода (частный) класс является видимым и поэтому может использоваться как тип возврата в сигнатуре метода. Однако этот метод можно вызвать только внутри этого класса. Код в каждом другом классе не может вызвать этот метод из-за невозможности доступа к этому частному классу. – Seelenvirtuose

ответ

3

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

public class TestClass { 
    public static void main(String[] args) throws Exception { 
    Outer.configure(); // this is valid 
    } 
} 

или если вы счастливы обратиться к результату как Object:

public class TestClass { 
    public static void main(String[] args) throws Exception { 
    Object o = Outer.configure(); // this is valid 
    } 
} 

компилятор позволяет это, потому что он не нарушает правила Java. Object - это просто общедоступный суперкласс вашего частного класса.

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

+0

+1 - Действительно, ваш второй пример иллюстрирует, что для этой (по-видимому) аномальной языковой функции может быть использовано. –

+0

На самом деле [там * есть * важное практическое использование] (http://stackoverflow.com/a/26038350/2711488) – Holger

+0

@ Хольгер Если я неправильно понимаю вашу точку зрения, то это именно то, что я говорю в последнем абзаце моего ответ. Чтобы этот шаблон можно было использовать для создания непрозрачных объектов, но есть лучшие способы сделать это. –

-1

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

private static class X{ 

} 

public static MyClass.X getX() { 
    return new MyClass.X(); 
} 

затем из другого класса, я что-то вроде

public void get() { 
    System.out.println(MyClass.getX()); 
} 

мне не нужно знать, что X тип здесь, потому что Println просто хочет его ToString(), который унаследованный от объекта.

Другой пример

private static class X implements Serializable{ 

} 

public static MyClass.X getX() { 
    return new MyClass.X(); 
} 

В этом случае я не знаю X, но я знаю, что это Serializable, так что я могу сделать что-то вроде

public void get() { 
    Serializable x= MyClass.getX(); 
} 

То, что я не могу сделать, конечно, следует использовать тип X извне MyClass, но до тех пор, пока он мне действительно не понадобится, это просто еще один экземпляр объекта

0

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

  • Логически группировки классов, которые используются только в одном месте
  • к увеличению капсулирования
  • Чтобы получить более читаемыми и в сопровождении код

Например, если это было конфиденциально, вы не можете сказать:

OuterClass.StaticNestedClass nestedObject = 
new OuterClass.StaticNestedClass(); 

Итак, вам нужен публичный метод доступа к нему в этом случае.

2

Существует строгое требование о том, что методы interface должны быть public. Поэтому, когда метод возвращает не- public типа выполняет в interface контракт он должен бытьpublic:

class Foo implements Supplier<NonPublicType> { 
    public NonPublicType get() { // must be public ! 
    … 
    } 
} 

Кроме того, это еще можно назвать этот метод из вне пакета, если объявить класс и метод public. Но результат должен быть присвоен доступному супер-типу класса не public, если он будет использоваться. Например. если NonPublicType из приведенного выше примера CharSequence вы можете сказать CharSequence cs=foo.get(); за пределами упаковки (если мы сменим Foo на номер public).

Обратите внимание, что этот метод может переопределить метод суперкласса, который возвращает тип public и возвращает более конкретный тип не public (aka Covariant return type). Этот метод может быть вызван из классов внутри одного и того же пакета, используя более специфический тип.

0

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

abstract class Super { 
    abstract void zipper(); 
} 

final class Outer { 

    private static final class Configurator extends Super { 
     private Configurator() { 
     } 

     @Override 
     void zipper() { 
      System.out.println("Zip!"); 
     } 
     public void zoomer() { 
      System.out.println("Zoom!"); 
     } 

    } 

    public static Configurator configure() { 
     return new Configurator(); 
    } 

} 

public final class PublicPrivate { 

    public static void main(final String[] args) { 
     /* Outer.configure returns an instance of Configurator, 
      a subclass of Super */ 
     final Super = Outer.configure(); 
     /* Configurator.zoomer() is not available, 
      because Configurator is private */ 
     // o.zoomer(); /* Uncomment this line and the compile will fail */ 
     /* But Super.zipper() is available, 
      in the form in which Configurator overrid it */ 
     o.zipper(); 
    } 

}