web-dev-qa-db-fra.com

interface comme paramètre de méthode dans Java

J'ai eu une interview il y a quelques jours et on m'a posé une question comme celle-ci.

Q: Inverser une liste chaînée. Le code suivant est donné:

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

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

    }
}

J'étais confus parce que je ne savais pas qu'un objet d'interface pouvait être utilisé comme paramètre de méthode. L'intervieweur a expliqué un peu mais je n'en suis toujours pas sûr. Quelqu'un pourrait-il m'éclairer?

44
zihaoyu

C'est en fait l'un des moyens les plus courants et les plus utiles d'utiliser une interface. L'interface définit un contrat, et votre code peut fonctionner avec n'importe quelle classe qui implémente l'interface, sans avoir à connaître la classe concrète - il peut même fonctionner avec des classes qui n'existaient pas encore lorsque le code a été écrit.

Il existe de nombreux exemples dans l'API standard Java, en particulier dans le cadre des collections. Par exemple, Collections.sort () peut trier tout ce qui implémente List interface (pas seulement ArrayList ou LinkedList, bien que l'implémentation de votre propre List soit rare) et dont le contenu implémente l'interface Comparable (pas seulement String ou les classes de wrapper numérique - et avoir votre propre classe implémentant Comparable à cet effet est assez commun).

59

Ce n'est pas l'interface "objet" qui est passée à la méthode, mais juste un objet normal. C'est juste une façon de dire "ce paramètre acceptera tout objet qui prend en charge cette interface". C'est équivalent à accepter un objet d'un type de classe de base, même si vous passez dans une sous-classe.

24
Ben Zotto

C'est ce qu'on appelle la programmation d'interfaces. Vous ne codez pas vers une classe d'implémentation spécifique de listes de nœuds, mais vers l'interface implémentée par toutes ces implémentations.

De cette façon, votre code fonctionnera toujours si quelqu'un écrit une nouvelle et bien meilleure implémentation de NodeList après avoir écrit votre méthode inverse et vous n'avez pas à adapter votre code pour chaque nouvelle implémentation de NodeList .

8
ahe

L'argument a besoin d'un objet, dont la classe implémente une interface (le paramètre).

Dans pseudo Java le code:

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

est égal à:

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

Remarque; en savoir plus sur les interfaces ici: http://Java.Sun.com/docs/books/tutorial/Java/IandI/interfaceAsType.html

6
Pindatjuh

Eu cette même confusion lors de l'apprentissage des choses lambda. Cette vidéo n'a pas expliqué le concept, mais c'est un moyen clair pour vous de voir comment cela fonctionne en termes de passage d'une interface en paramètre.

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

2
Yoho

Vous ne pouvez pas créer une instance (/ objet) d'une interface. Oui, vous pouvez passer Interface comme paramètre dans la fonction. Mais la question semble incomplète. L'interface n'est implémentée par aucune classe. Quelque chose manque. Si vous essayez d'exécuter cela, le compilateur n'affichera aucune erreur.

Mais, dans la méthode reverse (), vous devez créer une instance de classe qui implémente l'interface NodeList. J'espère que cela a du sens.

1
user4660857

Il s'agit d'une implémentation possible:

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());
        }
}

Sortie du programme:

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

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

Je suis très curieux de savoir si ce problème peut être résolu en utilisant une classe anonyme. Des idées?

1
jrook

Le principal avantage de l'utilisation des interfaces, à mon humble avis, est de pouvoir tester facilement. Supposons que vous ayez une interface appelée PatientManager.

Vous pouvez écrire des tests unitaires spécifiques pour des choses imaginables comme "CachingPatientManager" ou "LDAPPatientManager", le cas d'utilisation pourrait être innombrable.

L'avantage est que la programmation à l'interface devient hautement réutilisable et testable.

1
Nirmal