如何在 Java 的派生类构造函数中分配基类的最终变量?
-
20-09-2019 - |
题
我有一个基地 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
class'吸气剂由色调、饱和度和亮度构成。这就是事情停止工作的地方。
的构造函数 ColorHSL
需要做一些计算,然后设置值 _r
, _b
, , 和 _g
. 。但必须调用超级构造函数 前 进行任何计算。所以无参数 Color()
引入了构造函数,允许最终 _r
, _b
, , 和 _g
稍后设置。但是,无参数构造函数或设置(第一次,在构造函数内) ColorHSL
) 被 Java 编译器接受。
有没有办法解决这个问题,或者我必须删除 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;
}
}
的班级 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);
}
}
解决方案
是的,我可以看到如何 super(calculateRGB(...))
- 但 看起来你几乎没有从这里的继承中获得任何好处. 。我只是使用通用接口。RGB 和 HSV 不是两种不同的、可互换的颜色模型吗?
我认为Java问题背后有一个面向对象的分析问题。为什么要使用继承?
如果您需要做的只是交替操纵颜色, 您可能会发现您根本没有从继承中受益 (它只是为超类创建了将 HSV 映射回 RGB 的开销)...如果您只需要可互换的颜色模型,请考虑使用 Color 接口而不是从 RGB 继承。
如果不了解 Color 对象的实际用途,就很难提出更好的设计。现在看起来继承的成本(调用超级构造函数中的颜色模型转换)将超过重用的唯一好处 normalize
.
其他提示
必须在声明类型的构造函数完成时分配最终变量。因此,你不能在子类中分配super的final字段。
但是,您可以在子类中的静态工厂方法中进行转换:
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);
}
}
据我所知,完成此操作的唯一方法是将超级构造函数的调用与计算 r、g 和 b 的函数嵌套在一起:
super(calculateRGB(...))
因此,您可能会考虑向 Color 添加一个构造函数,将 RGB 值作为数组。
Java 中不能有抽象构造函数,因此除非您可以将所有计算放入对 super 的调用中,否则您无法对当前设计执行您想要执行的操作,因为最终变量必须按时分配声明构造函数已完成。
另一种方法是分解一些用于创建 Color 对象(工厂模式)的方法,该方法接受参数,在构造函数外部进行计算,然后您可以调用 super() 作为第一个参数。
例如 - 如果您的颜色类别有以下内容
public Color static createHSLColor(int h, int s, int v) {
// All the calculation here
return new ColorHSL(h,s,v);
}
然后,您可以使构造函数不公开(可能是受保护的),然后创建对象的唯一方法是通过工厂方法。
您可以做的一件事是拥有一个代表计算器的构造函数参数。例如:
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);
}