Атрибуты / переменные-члены в интерфейсах? - PullRequest
62 голосов
/ 05 сентября 2011

Я хотел бы знать, есть ли способ сделать обязательным для класса реализатора объявление объектов / примитивов, как они делают с методами. например ,:

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) {   
    }

}

В приведенном выше методе, как мы можем заставить класс Tile объявлять атрибуты высоты и ширины, используя интерфейс? Почему-то я хочу сделать это только с интерфейсом!

Я изначально думал об использовании его с наследованием. Но дело в том, что мне приходится иметь дело с 3 классами.!

  1. Прямоугольник
  2. Плитка
  3. JLabel.!

 class Tile extends JLabel implements Rectangle {}

будет работать.!

но

class Tile extends JLabel extends Rectangle {}

не так!

Ответы [ 7 ]

74 голосов
/ 05 сентября 2011

Цель интерфейса - указать публичный API.Интерфейс не имеет состояния.Любые переменные, которые вы создаете, действительно являются константами (поэтому будьте осторожны при создании изменяемых объектов в интерфейсах).

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

Поскольку выпросто указав, какие методы должны быть реализованы, нет понятия состояния (нет переменных экземпляра).Если вы хотите, чтобы у каждого класса была определенная переменная, вам нужно использовать абстрактный класс.

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

Краткий ответ - вы не можете делать то, что хотите, потому что это «неправильно» в Java.

Редактировать:

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

альтернативная версия будет:

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 
{
}
8 голосов
/ 05 сентября 2011

Вы можете сделать это только с абстрактным классом, а не с интерфейсом.

Объявите Rectangle как abstract class вместо interface и объявите методы, которые должны быть реализованы подпрограммой-класс как public abstract.Тогда класс Tile расширяет класс Rectangle и должен реализовывать абстрактные методы из Rectangle.

7 голосов
/ 05 сентября 2011

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

( Переменные могут быть определены в интерфейсах , но они не ведут себя так, как могли быожидается: они рассматриваются как final static.)

Счастливое кодирование.

6 голосов
/ 18 сентября 2014

Java 8 представила методы по умолчанию для интерфейсов, использующих методы для тела. Согласно ООП интерфейсы должны действовать как контракт между двумя системами / сторонами.

Но все же я нашел способ добиться сохранения свойств в интерфейсе. Я признаю, что это довольно уродливая реализация.

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

Этот интерфейс ужасен. Для хранения простого свойства потребовалось два хеш-карты, и каждый хеш-файл по умолчанию создает 16 записей по умолчанию. Кроме того, когда реальный объект разыменовывается, JVM дополнительно необходимо удалить эту слабую ссылку.

2 голосов
/ 05 сентября 2011

В Java вы не можете. Интерфейс связан с методами и сигнатурой, он не имеет отношения к внутреннему состоянию объекта - это вопрос реализации. И это тоже имеет смысл - я имею в виду, просто потому, что существуют определенные атрибуты, это не значит, что они должны использоваться реализующим классом. getHeight может фактически указывать на переменную ширины (при условии, что разработчик является садистом).

(Как примечание - это относится не ко всем языкам, ActionScript допускает объявление псевдоатрибутов, и я считаю, что C # тоже)

0 голосов
/ 12 сентября 2014

Том сказал что-то важное:

если вы используете концепцию has-a, вы избегаете этой проблемы.

Действительно, если вместо использования extends и реализует , вы определяете два атрибута, один из которых имеет тип прямоугольник, а другой тип JLabel в вашем классе Tile, тогда вы можете определите Rectangle как интерфейс или класс.

Кроме того, я бы обычно поощрял использование интерфейсов в связи с has-a, но, полагаю, в вашей ситуации это было бы излишним. Тем не менее, вы единственный, кто может принять решение по этому вопросу (компромиссная гибкость / чрезмерная инженерия).

0 голосов
/ 05 сентября 2011

Поля в интерфейсах неявно public static final.(Также методы неявно общедоступны, поэтому вы можете удалить ключевое слово public.) Даже если вы используете абстрактный интерфейс вместо интерфейса, я настоятельно рекомендую сделать все неконстантным (public static final для ссылки на примитивный или неизменный объект)private.В более общем смысле «предпочитайте композицию наследованию» - Tile is-not-a Rectangle (конечно, вы можете играть в игры в слова с помощью «is-a» и «has-a»).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...