2015-08-12 5 views
1

Я озадачен некоторым исходным кодом, на который я смотрю, и я пытаюсь получить лучшее понимание. Код ниже, но он также доступен at Ideone, где вы можете играть с ним самостоятельно.Определение внутреннего интерфейса в Java как переменной экземпляра для класса

import java.util.*; 
import java.lang.*; 
import java.io.*; 

class Ideone 
{ 
    public interface Fruit { 
     public void apple(); 
    } 

    Fruit interfaceFruit; 

    public void apple(){ 
     System.out.println("apple"); 
     if (interfaceFruit != null) { 
      interfaceFruit.apple(); 
     } 
    } 

    public void setFruit(Fruit f) { 
     interfaceFruit = f; 
    } 

    public static void main (String[] args) throws java.lang.Exception 
    { 
     Ideone test = new Ideone(){ 
      @Override 
      public void apple() { 
       System.out.println("local override of apple"); 
      } 
     }; 
     System.out.println("1) ---"); 
     test.apple(); 

     Ideone test2 = new Ideone(); 
     System.out.println("2) ---"); 
     test2.apple(); 

     test2.setFruit(new Fruit() { 
      @Override 
      public void apple() { 
       System.out.println("interface override of apple"); 
      } 
     }); 
     System.out.println("3) ---"); 
     test2.apple(); 
    } 
} 

Выход:

1) --- 
local override of apple 
2) --- 
apple 
3) --- 
apple 
interface override of apple 

Что именно происходит в этом коде? Существует интерфейс, объявленный внутри класса (так, внутренний интерфейс, правильно?), А затем интерфейс объявляется как переменная экземпляра для класса. Здесь я смущен.

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

+0

Связанный: http://stackoverflow.com/questions/71625/why-would-a-static-inner-interface-be-used-in-java – jaco0646

+1

Интерфейс ** не ** хранится в переменной. Переменная объявляется как интерфейс ** type **. –

+1

Важно помнить о интерфейсах, которые для них являются лишь лексикой, другими словами: вложенные интерфейсы всегда неявно «статичны». Строка, которую вы задаете, представляет собой просто объявление поля и полностью не зависит от декларации интерфейса. Вы можете переместить свой интерфейс в отдельный исходный файл, и вы все равно можете объявить поле типа «Фрукты». – biziclop

ответ

1

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

What I'm not totally getting is how or why the interface is stored in an instance variable to the class

Не хранится, это определяется. Вы можете иметь внутренние классы и интерфейсы, определенные в классе; несколько примеров:

  • Классы и интерфейсы используются только во внутренней логике класса и не нужны в другом месте; обычно они определяются как частные. Работает более или менее так же, как анонимный класс.

  • Классы и интерфейсы тесно связаны с классом. Например, вы определяете класс и реализацию Comparator для этого компонента; поскольку такой Comparator полезен только для элементов этого класса, вы определяете это как вложенный класс. Он работает так же, как еще один уровень «упаковки».

Например, я адепт контролировать значения, передаваемые мои методы так, когда это имеет смысл, я такие вещи, как (очень тупой пример)

public class Calendar { 
    public static enum WeekDay { 
    MONDAY, 
    ... 
    SUNDAY; 
    } 

    public Date getNextDay(WeekDay weekDay) { 
    ... 
    } 
} 

Перечисление полезно только доступ к логике класса, поэтому он определен внутри класса.

И пример в Java API - это класс java.util.Map.Entry<K,V>, который используется реализациями Map, чтобы предоставить доступ к парам значений ключей, которые они хранят.

+0

Что касается комментария «сохраненный vs определенный» в вашем ответе, я говорил об этой строке «Fruit interfaceFruit;» Интерфейс объявлен как переменная экземпляра no? Затем он устанавливается позже через 'setFruit()'. Насколько вы говорите: «Это не обычный код, это просто упражнение, чтобы вы могли видеть, как оно работает». Это заставляет меня чувствовать себя немного лучше. Я написал этот код, но это просто упростило то, что действительно находится в нашей базе кода. –

+1

'Интерфейс объявлен как переменная экземпляра no?' Нет. Интерфейс полностью определен интерфейсом Fruit {...} ', что' Fruit fruit' делает, чтобы * объявить переменную *, которая может ссылаться на экземпляр класс, который реализует 'Fruit' (или null, если значение не присвоено). – SJuan76

+2

Интерфейс объявлен как внутренний, и есть переменная экземпляра типа Fruit, объявленная в классе. Более того, если вы сделаете это явным или нет, любой внутренний интерфейс будет считаться статическим внутренним интерфейсом. – iullianr

1

Объявление:

Fruit interfaceFruit; 

Просто говорит: «объявить переменную interfaceFruit, которая будет использоваться для хранения ссылки на объект, который реализует Fruit

Такая переменная может содержать ссылку. любой класс, при условии, что он реализует интерфейс.И что вы можете сделать с ним, это доступ ко всем методам и константам, которые определены в интерфейсе, потому что Java знает, что независимо от класса, он будет иметь переопределяющие методы, соответствующие объявлениям в Fruit (который в этом случае имеет только apple()).

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

List<Integer> list; 

(Где List это интерфейс java.util.List), а затем присвоить ей конкретную реализацию:

list = new LinkedList<Integer>(); 
list = new ArrayList<Integer>(); 
list = Arrays.asList(1,5,8,9); 

Какой класс объект, который назначен на (и в случае Arrays.asList вы даже не знаете, какой класс будет), он будет иметь все операции, которые определяет java.util.List - например, add() или iterator().

То же самое относится к вашему интерфейсу Fruit. Тот факт, что он является вложенным интерфейсом, не так важен.

Смежные вопросы