Как я могу назначить конечные переменные базового класса в конструкторе производного класса в Java? - PullRequest
0 голосов
/ 14 ноября 2009

У меня есть базовый Color класс, который выглядит примерно так. Класс спроектирован так, чтобы быть неизменным, поэтому в результате у него есть final модификаторы и нет сеттеров:

public class Color
{
    public static Color BLACK   = new Color(0, 0, 0);
    public static Color RED = new Color(255, 0, 0);
    //...
    public static Color WHITE   = new Color(255, 255, 255);

    protected final int _r;
    protected final int _g;
    protected final int _b;

    public Color(int r, int b, int g)
    {
        _r = normalize(r);
        _g = normalize(g);
        _b = normalize(b);
    }

    protected Color()
    {

    }

    protected int normalize(int val)
    {
        return val & 0xFF;
    }
    // getters not shown for simplicity
}

Производный от этого класса является классом ColorHSL, который в дополнение к обеспечению Color класса 'получает его, имеет оттенок, насыщенность и яркость. Здесь все перестает работать.

Конструктор ColorHSL должен выполнить некоторые вычисления, а затем установить значения _r, _b и _g. Но супер-конструктор должен быть вызван , прежде чем будут выполнены какие-либо вычисления. Таким образом, был введен конструктор Color() без параметров, позволяющий позднее устанавливать конечные значения _r, _b и _g Однако ни конструктор без параметров, ни параметр (впервые в конструкторе ColorHSL) не принимаются компилятором Java.

Есть ли способ обойти эту проблему, или мне нужно удалить модификатор final из _r, _b и _g?


Edit:

В итоге я выбрал базовый абстрактный класс Color, содержащий данные как RGB, так и HSL. Базовый класс:

public abstract class Color
{
    public static Color WHITE   = new ColorRGB(255, 255, 255);
    public static Color BLACK   = new ColorRGB(0, 0, 0);
    public static Color RED = new ColorRGB(255, 0, 0);
    public static Color GREEN   = new ColorRGB(0, 255, 0);
    public static Color BLUE    = new ColorRGB(0, 0, 255);
    public static Color YELLOW  = new ColorRGB(255, 255, 0);
    public static Color MAGENTA = new ColorRGB(255, 0, 255);
    public static Color CYAN    = new ColorRGB(0, 255, 255);

    public static final class RGBHelper
    {
        private final int   _r;
        private final int   _g;
        private final int   _b;

        public RGBHelper(int r, int g, int b)
        {
            _r = r & 0xFF;
            _g = g & 0xFF;
            _b = b & 0xFF;
        }

        public int getR()
        {
            return _r;
        }

        public int getG()
        {
            return _g;
        }

        public int getB()
        {
            return _b;
        }
    }

    public final static class HSLHelper
    {
        private final double    _hue;
        private final double    _sat;
        private final double    _lum;

        public HSLHelper(double hue, double sat, double lum)
        {
            //Calculations unimportant to the question - initialises the class
        }

        public double getHue()
        {
            return _hue;
        }

        public double getSat()
        {
            return _sat;
        }

        public double getLum()
        {
            return _lum;
        }
    }

    protected HSLHelper HSLValues   = null;
    protected RGBHelper RGBValues   = null;

    protected static HSLHelper RGBToHSL(RGBHelper rgb)
    {
        //Calculations unimportant to the question
        return new HSLHelper(hue, sat, lum);
    }

    protected static RGBHelper HSLToRGB(HSLHelper hsl)
    {
        //Calculations unimportant to the question
        return new RGBHelper(r,g,b)
    }

    public HSLHelper getHSL()
    {
        if(HSLValues == null)
        {
            HSLValues = RGBToHSL(RGBValues);
        }
        return HSLValues;
    }

    public RGBHelper getRGB()
    {
        if(RGBValues == null)
        {
            RGBValues = HSLToRGB(HSLValues);
        }
        return RGBValues;
    }
}

Классы RGBColor и HSLColor затем наследуются от Color, реализуя простой конструктор, который инициализирует элементы RGBValues и HSLValues. (Да, я знаю, что базовый класс if-ily содержит статический экземпляр производного класса)

public class ColorRGB extends Color
{
    public ColorRGB(int r, int g, int b)
    {
        RGBValues = new RGBHelper(r,g,b);
    }
}

public class ColorHSL extends Color
{
    public ColorHSL(double hue, double sat, double lum)
    {
        HSLValues = new HSLHelper(hue,sat,lum);
    }
}

Ответы [ 5 ]

8 голосов
/ 14 ноября 2009

Окончательная переменная должна быть назначена ко времени завершения конструктора объявленного типа. Следовательно, вы не можете назначать конечные поля super в подклассе.

Однако вы можете выполнить преобразование в методе статической фабрики в подклассе:

class HSLColor {
    private HSLColor(int r, int g, int b) { super(r,g,b);}

    static HSLColor create(int h, int s, int l) {
        // conversion code here
        return new HSLColor(r,g,b);
    }
}
3 голосов
/ 14 ноября 2009

Насколько я знаю, единственный способ сделать это - поместить вызов супер-конструктора в функцию для вычисления r, g и b:

super(calculateRGB(...))

Так что вы можете рассмотреть возможность добавления конструктора в Color, который принимает значения RGB в виде массива.

2 голосов
/ 14 ноября 2009

Да, я вижу, как super(calculateRGB(...)) - но выглядит так, как будто вы практически ничего не получаете от наследования .Я бы просто использовал общий интерфейс.Разве RGB и HSV не две разные взаимозаменяемые цветовые модели?

Я думаю, что проблема Java связана с проблемой объектно-ориентированного анализа.Почему вы используете наследование?

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

Не зная, для чего вы собираетесь использовать объекты Color, сложнопредложить лучший дизайн.Сейчас кажется, что стоимость наследования (преобразования цветовой модели в вызове супер-конструктора) перевесит единственное преимущество повторного использования normalize.

1 голос
/ 14 ноября 2009

Одна вещь, которую вы могли бы сделать, это иметь параметр конструктора, который представляет калькулятор. Так, например:

public Color(int r, int g, int b, Calc calc) {
   _r = calc.normalize(r);
   _g = calc.normalize(g);
   _b = calc.normalize(b);
}

Это может полностью устранить необходимость в подклассе. Вы можете объявить конструкторов:

public Color(int r, int g, int b) { 
  this(r,g,b, defaultCalc);
}

Или даже предоставить статические конструкторы стиля:

public static Color hslColor(int r, int g, int b) {
    return new Color(r,g,b, hslCalc);
}
1 голос
/ 14 ноября 2009

У вас не может быть абстрактного конструктора в Java, поэтому, если вы не можете поместить все вычисления в вызов super, вы не сможете делать то, что хотели бы сделать с текущим дизайном, поскольку должна быть назначена конечная переменная к тому времени, как объявленный конструктор завершился.

Альтернативой может быть выделение некоторых методов для создания объектов Color (фабричный шаблон), который принимает аргументы, выполняет вычисления вне конструктора, а затем вы можете вызвать super () в качестве первого аргумента.

Например - если у вас есть следующее в вашем классе цвета

public Color static createHSLColor(int h, int s, int v) {
   // All the calculation here

   return new ColorHSL(h,s,v);
}

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

...