интерфейс в качестве параметра метода в Java

У меня было интервью несколько дней назад, и мне задали такой вопрос.

Q: обратный связанный список. Следующий код:

public class ReverseList { 
    interface NodeList {
        int getItem();
        NodeList nextNode();
    }
    void reverse(NodeList node) {

    }
    public static void main(String[] args) {

    }
}

Я был смущен, потому что я не знал, что объект интерфейса может использоваться в качестве параметра метода. Интервьюер немного объяснил, но я все еще не уверен в этом. Может, кто-нибудь просветит меня?

8 ответов


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

есть много примеров в стандартном API Java, особенно в рамках коллекций. Например, сборники.sort () можете сортировать все, что реализует List интерфейс (не просто ArrayList или LinkedList, хотя и реализуя свой собственный List является необычным) и чье содержимое реализует Comparable интерфейс (не просто String или числовые классы обертки-и наличие собственного класса implement Comparable для этого есть совсем common).


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


это называется программированием на интерфейсы. Вы не кодируете определенный класс реализации списков узлов, а интерфейс, реализованный всеми этими реализациями.

таким образом, ваш код будет работать, если кто-то напишет новую и гораздо лучшую реализацию NodeList после того как вы написали свой обратный метод и вам не придется адаптировать ваш код для каждой новой реализации NodeList.


аргументу нужен объект, класс которого реализует интерфейс (параметр).

на псевдо Java код:

void reverse(NodeList node) {
    // your code
}

равен:

reverse(x) {
    if(x == null || x instanceof NodeList) {
         // your code
    }else throw new RuntimeException("Some sort of error.");
}

Примечание; подробнее о интерфейсах здесь:http://java.sun.com/docs/books/tutorial/java/IandI/interfaceAsType.html


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

https://www.youtube.com/watch?v=mk3erzL70yM


главным преимуществом использования интерфейсов, IMHO, является возможность легко тестировать. Предположим, у вас есть интерфейс с именем PatientManager.

вы можете написать конкретные модульные тесты для воображаемых вещей, таких как" CachingPatientManager "или" LDAPPatientManager", вариант использования может быть мириад.

преимущество заключается в том, что программирование интерфейса становится многоразовым и проверяемым.


вы не можете создать экземпляр (объект ) интерфейса. Да, вы можете передать интерфейс в качестве параметра в функции. Но вопрос кажется неполным. Интерфейс не реализуется ни одним классом. Чего-то не хватает. Если вы попытаетесь запустить это, компилятор не покажет никакой ошибки.

но в методе reverse () вам нужно создать экземпляр класса, который реализует интерфейс NodeList. Надеюсь, это имеет смысл.


Это одна из возможных реализаций:

public class ReverseList { 
interface NodeList {
    int getItem();
    NodeList nextNode();
}

static class Node implements NodeList {
    private int item;
    private Node next;

    @Override
    public int getItem() {
        return item;
    }

    public void setItem(int si) {
        item = si;
    }

    @Override
    public NodeList nextNode() {
        return this.next;
    }

    public void setNext(Node n) {this.next=n;}

}

Node reverse(NodeList head) {
    Node node = (Node) head;
    Node previous = null;
    while(node.nextNode() !=null) {
        Node tempNext = (Node) node.nextNode();
        node.setNext(previous);
        previous = node;
        node = tempNext;
    }
    node.setNext(previous);
    return node;

}
public static void main(String[] args) {
    //Initialization block
    ReverseList rl = new ReverseList();
    Node n1= new Node(); n1.setItem(1);
    Node n2=new Node(); n2.setItem(2);
    Node n3 =new Node(); n3.setItem(3);
    n1.setNext(n2); n2.setNext(n3); n3.setNext(null);

    //Reversing the list
    System.out.println("Before reversal");      
    System.out.println(n1.getItem() +"->" 
                    + n1.nextNode().getItem() + "->"
                    + n1.nextNode().nextNode().getItem() + "->"
                    +n1.nextNode().nextNode().nextNode());


    rl.reverse(n1);

    System.out.println("\nAfter reversal");
    System.out.println(n3.getItem() +"->" 
            + n3.nextNode().getItem() + "->"
            + n3.nextNode().nextNode().getItem() + "->"
            +n3.nextNode().nextNode().nextNode());
        }
}

программа:

Before reversal
1->2->3->null

After reversal
3->2->1->null

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