如何在Java中的派生类'构造函数中分配基类的最终变量?

时间:2009-11-14 14:31:05

标签: java inheritance immutability final

我有一个基类Color类看起来像这样。该类被设计为不可变的,因此结果具有final修饰符且没有setter:

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类'getter之外,还有色调,饱和度和亮度构造。事情就这样停止了。

ColorHSL的构造函数需要进行一些计算,然后设置_r_b_g的值。但是,在进行任何计算之前,必须将超级构造函数称为。因此,引入了无参数Color()构造函数,允许稍后设置最终_r_b_g。但是,Java编译器都不接受无参数构造函数或设置(第一次在ColorHSL的构造函数内)。

是否有解决此问题的方法,或者是否必须从final_r_b删除_g修饰符?


编辑:

最后,我选择了一个基础抽象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;
    }
}

然后RGBColorHSLColor的类派生自Color,实现了一个初始化RGBValuesHSLValues成员的简单构造函数。 (是的,我知道基类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 个答案:

答案 0 :(得分:8)

必须在声明类型的构造函数完成时指定最终变量。因此,您无法在子类中指定超级的最终字段。

但是,您可以在子类中的静态工厂方法中进行转换:

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

答案 1 :(得分:3)

据我所知,完成此操作的唯一方法是使用函数嵌套超级构造函数的调用来计算r,g和b:

super(calculateRGB(...))

因此,您可以考虑向Color添加构造函数,将RGB值作为数组。

答案 2 :(得分:2)

是的,我可以看到super(calculateRGB(...)) - 但看起来好像你从这里获得的遗产几乎没有什么。我只是使用一个通用的界面。 RGB和HSV不是两种不同的,可互换的颜色模型吗?

我认为Java问题背后存在面向对象的分析问题。你为什么要使用继承?

如果你只需要交替操作一个颜色,你可能会发现你根本没有从继承中受益(它只会产生将HSV映射回RGB的开销超类)...如果您只想要可互换的颜色模型,请考虑使用Color接口而不是从RGB继承。

如果没有看到实际使用Color对象的内容,很难建议更好的设计。现在,看起来继承的成本(调用超级构造函数时的颜色模型转换)将超过重用normalize的唯一好处。

答案 3 :(得分:1)

你不能在Java中有一个抽象的构造函数,所以除非你能把所有的计算都放到super的调用中,否则你不能做你想做的当前设计,因为必须指定一个最终的变量当声明构造函数完成时。

另一种方法是分解一些创建Color对象的方法(工厂模式),它接受参数,在构造函数外部进行计算,然后你可以调用super()作为第一个参数。 / p>

例如 - 如果您在Color类上有以下内容

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

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

然后,您可以使构造函数不公开(可能受到保护),然后可以通过工厂方法创建对象的唯一方法。

答案 4 :(得分:1)

你可以做的一件事是有一个构造函数参数代表计算器。例如:

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