Attribute / Mitgliedsvariablen in Schnittstellen?

73

Ich möchte wissen, ob es für die Implementiererklasse eine Möglichkeit gibt, die Objekthandles / Grundelemente wie bei Methoden zu deklarieren. für zB:

public interface Rectangle {    
    int height = 0;
    int width = 0;

    public int getHeight();
    public int getWidth();
    public void setHeight(int height);
    public void setWidth(int width);                
}


public class Tile implements Rectangle{
    @Override
    public int getHeight() {
        return 0;
    }

    @Override
    public int getWidth() {
        return 0;
    }

    @Override
    public void setHeight(int height) {
    }

    @Override
    public void setWidth(int width) {   
    }

}

Wie können wir in der obigen Methode die Tile-Klasse dazu zwingen, Höhen- und Breitenattribute über die Schnittstelle zu deklarieren? Aus irgendeinem Grund möchte ich es nur mit Schnittstelle machen!

Ich dachte anfangs daran, es mit Vererbung zu verwenden. Aber was ist, ich muss mich mit 3 Klassen auseinandersetzen.!

  1. Rechteck
  2. Fliese
  3. JLabel.!

 

 class Tile extends JLabel implements Rectangle {}

würde funktionieren.!

aber

class Tile extends JLabel extends Rectangle {}

würde nicht.!

Shrey
quelle
8
Ihre Schnittstelle ist falsch. Es darf keine Attribute enthalten. Vielleicht möchten Sie eine abstrakte Klasse verwenden?
pvoosten
10
@lbp Das wird eigentlich kompiliert ;-) Es ist nur ... nicht das "erwartete" Verhalten. Der Compiler nimmt einen final staticModifikator an / wendet ihn an .
1
Möglicherweise möchten Sie Height anstelle von Hieght und Hight verwenden. Höhe ist das Partizip der Vergangenheit von Highten, Hihten, zu rufen, gerufen zu werden. ;)
Peter Lawrey
Wenn Sie Accessoren bereitstellen, warum sollten Sie die Variablen verfügbar machen?
Jonathan Leffler
Sie können Konstanten in einer Schnittstelle haben, aber das war es auch schon, und selbst dann wäre es schwierig, ein Szenario zu finden, in dem die Verwendung solcher Konstanten nicht erfunden wäre.
Wim Ombelets

Antworten:

93

Der Sinn einer Schnittstelle besteht darin, die öffentliche API anzugeben. Eine Schnittstelle hat keinen Status. Alle Variablen, die Sie erstellen, sind wirklich Konstanten (seien Sie also vorsichtig, wenn Sie veränderbare Objekte in Schnittstellen erstellen).

Grundsätzlich sagt eine Schnittstelle, dass hier alle Methoden aufgeführt sind, die eine Klasse, die sie implementiert, unterstützen muss. Es wäre wahrscheinlich besser gewesen, wenn die Entwickler von Java Konstanten in Schnittstellen nicht zugelassen hätten, aber zu spät, um dies jetzt zu beseitigen (und es gibt einige Fälle, in denen Konstanten in Schnittstellen sinnvoll sind).

Da Sie nur angeben, welche Methoden implementiert werden müssen, gibt es keine Vorstellung vom Status (keine Instanzvariablen). Wenn Sie möchten, dass jede Klasse eine bestimmte Variable hat, müssen Sie eine abstrakte Klasse verwenden.

Schließlich sollten Sie im Allgemeinen keine öffentlichen Variablen verwenden, daher ist die Idee, Variablen in eine Schnittstelle einzufügen, zunächst eine schlechte Idee.

Kurze Antwort - Sie können nicht tun, was Sie wollen, weil es in Java "falsch" ist.

Bearbeiten:

class Tile 
    implements Rectangle 
{
    private int height;
    private int width;

     @Override
    public int getHeight() {
        return height;
    }

    @Override
    public int getWidth() {
        return width;
    }

    @Override
    public void setHeight(int h) {
        height = h;
    }

    @Override
    public void setWidth(int w) { 
        width = w;  
    }
}

Eine alternative Version wäre:

abstract class AbstractRectangle 
    implements Rectangle 
{
    private int height;
    private int width;

     @Override
    public int getHeight() {
        return height;
    }

    @Override
    public int getWidth() {
        return width;
    }

    @Override
    public void setHeight(int h) {
        height = h;
    }

    @Override
    public void setWidth(int w) { 
        width = w;  
    }
}

class Tile 
    extends AbstractRectangle 
{
}
TofuBeer
quelle
kk! Also, wie gehe ich vor? (Ich habe die Frage bearbeitet, bitte überprüfen)!
Shrey
Das wird wohl helfen ..! thnxx :)
Shrey
Damit bleibt jedoch das Problem, dass Sie in der konkreten Implementierung nicht mit der Höhe herumspielen können. Sie wissen also, dass Sie eine Höhe benötigen, Sie wissen, dass Sie in der Lage sein müssen, die Höhe zurückzugeben, aber Sie können es nicht jeder Implementierung überlassen, die Höhe zu berechnen (was meiner Meinung nach den Zweck zunichte macht). Natürlich können Sie getHeightabstrakt deklarieren und überhaupt keine Höhe deklarieren, aber dann müssen Sie den Rückkehrcode jedes Mal wiederholen, damit Sie die Variable privat halten können.
Alianos
1
-1 für "Der Zweck einer Schnittstelle besteht darin, die öffentliche API anzugeben. Eine Schnittstelle hat keinen Status."; Eine Schnittstelle hat keinen Status, aber für eine Schnittstelle sind öffentliche Mitgliedsvariablen Teil der öffentlichen API des Objekts. Leider gibt eine öffentliche Mitgliedsvariable in Java-Land etwas über die Implementierung dieser API an (nämlich, dass es sich um eine speicherinterne Variable handelt), anstatt sich die Flexibilität zu lassen, die Implementierung zu ändern. Mit anderen Worten, obj.Propertykann niemals einen anderen Code aufrufen , kann aber obj.Property()& die beiden werden auf der Schnittstellenebene unterschiedlich behandelt.
weberc2
12

Für Schnittstellen müssen keine Instanzvariablen definiert werden - nur Methoden.

( Variablen können in Schnittstellen definiert werden , verhalten sich jedoch nicht wie erwartet: Sie werden wie folgt behandelt final static.)

Viel Spaß beim Codieren.


quelle
9

In Java 8 wurden Standardmethoden für Schnittstellen eingeführt, mit denen Sie die Methoden verwenden können. Laut OOPs sollten Schnittstellen als Vertrag zwischen zwei Systemen / Parteien fungieren.

Trotzdem habe ich einen Weg gefunden, um Eigenschaften in der Benutzeroberfläche zu speichern. Ich gebe zu, es ist eine hässliche Implementierung.

   import java.util.Map;
   import java.util.WeakHashMap;

interface Rectangle
{

class Storage
{
    private static final Map<Rectangle, Integer> heightMap = new WeakHashMap<>();
    private static final Map<Rectangle, Integer> widthMap = new WeakHashMap<>();
}

default public int getHeight()
{
    return Storage.heightMap.get(this);
}

default public int getWidth()
{
    return Storage.widthMap.get(this);
}

default public void setHeight(int height)
{
    Storage.heightMap.put(this, height);
}

default public void setWidth(int width)
{
    Storage.widthMap.put(this, width);
}
}

Diese Schnittstelle ist hässlich. Zum Speichern einfacher Eigenschaften wurden zwei Hashmaps benötigt, und jede Hashmap erstellt standardmäßig 16 Einträge. Wenn ein reales Objekt dereferenziert wird, muss JVM diese schwache Referenz zusätzlich entfernen.

Dungeon Hunter
quelle
2
Ziemlich interessant, aber komisch :). Sie können sogar die innere Klasse eliminieren und nur statische endgültige Karten in Ihrer Benutzeroberfläche selbst speichern.
iMysak
8

Sie können dies nur mit einer abstrakten Klasse tun, nicht mit einer Schnittstelle.

Deklarieren Sie Rectangleals abstract classanstelle von interfaceund deklarieren Sie die Methoden, die von der Unterklasse implementiert werden müssen, als public abstract. Dann erweitert class Tiledie Klasse Rectangleund muss die abstrakten Methoden von implementieren Rectangle.

Simon C.
quelle
Ich dachte anfangs daran, es mit Vererbung zu verwenden. Aber was ist, ich muss mich mit 3 Klassen auseinandersetzen.! 1. Rechteck 2. Fliese 3. JLabel.! Klasse Tile erweitert JLabel implementiert Rectangle {} würde funktionieren.! aber Klasse Tile erweitert JLabel erweitert Rectangle {} woud nicht.!
Shrey
2

In Java können Sie nicht. Schnittstelle hat mit Methoden und Signatur zu tun, es hat nicht mit dem internen Zustand eines Objekts zu tun - das ist eine Implementierungsfrage. Und das macht auch Sinn - ich meine, nur weil bestimmte Attribute existieren, heißt das nicht, dass sie von der implementierenden Klasse verwendet werden müssen. getHeight könnte tatsächlich auf die width-Variable verweisen (vorausgesetzt, der Implementierer ist ein Sadist).

(Hinweis: Dies gilt nicht für alle Sprachen. ActionScript ermöglicht die Deklaration von Pseudoattributen, und ich glaube, dass C # dies auch tut.)

cwallenpoole
quelle
1
In C # können Getter / Setter (die eigentlich nur spezielle Methodenaufrufe sind) in Schnittstellen sein - Instanzvariablen selbst nicht.
Das habe ich mir gedacht (und deshalb habe ich Pseudo gesagt). ActionScript macht dasselbe.
Cwallenpoole
kk !! Vererbung statt Implementierung würde helfen.! Aber das würde mir ein neues Problem aufwerfen ..! Können Sie mir den Weg vorschlagen, wie ich vorgehen soll? (siehe die bearbeitete Frage) Danke !!
Shrey
0

Felder in Schnittstellen sind implizit public static final. (Auch Methoden sind implizit öffentlich, sodass Sie das publicSchlüsselwort löschen können.) Selbst wenn Sie eine abstrakte Klasse anstelle einer Schnittstelle verwenden, empfehle ich dringend, alle nicht konstant zu machen ( public static finaleiner primitiven oder unveränderlichen Objektreferenz) private. Im Allgemeinen "Komposition der Vererbung vorziehen" - ein Tileis-not-a Rectangle(natürlich können Sie Wortspiele mit "is-a" und "has-a" spielen).

Tom Hawtin - Tackline
quelle
Form der Fliese ist-ein Rechteck .. ?? (ist-a und hat-a verwirrt mich am meisten)
Shrey
Ich denke, A sollte B genau dann erweitern, wenn die Antwort auf 'ist jedes B a A' wahr ist ..! Lesen Sie aus Effective Java ..
Shrey
1
Wie kann B ein A sein, wenn A B erweitert? Sie haben es von hinten nach vorne.
RichieHH
0

Tom hat etwas Wichtiges gesagt:

Wenn Sie das Has-A-Konzept verwenden, vermeiden Sie das Problem.

Wenn Sie anstelle von Erweiterungen und Implementierungen zwei Attribute definieren, eines vom Typ Rechteck, eines vom Typ Typ JLabelin Ihrer TileKlasse, können Sie a Rectangleentweder als Schnittstelle oder als Klasse definieren.

Darüber hinaus würde ich normalerweise die Verwendung von Schnittstellen in Verbindung mit has-a fördern, aber ich denke, es wäre ein Overkill in Ihrer Situation. Sie sind jedoch der einzige, der über diesen Punkt entscheiden kann (Kompromissflexibilität / Überentwicklung).

bal
quelle