JavaFX中Node对象克隆的属性传递有效性验证问询
JavaFX Node克隆的属性独立性验证
我有一个JavaFX Node 对象,想要通过代码对其进行克隆。我知道Java变量是引用类型,直接执行 clonedNode = oldNode 会让 clonedNode 指向原对象的引用。JavaFX没有内置的Node克隆方法,所以我自己写了一个克隆函数(仅处理Circle、Rectangle、Polygon、Line、Text和Group类型,关注填充、描边、文本及位置属性),代码如下:
public static Node NodeClone(Node oldNode) { Node clonedNode = null; if (oldNode.getClass() == Circle.class) { Circle oldCircle = (Circle)oldNode; Circle clonedCircle = new Circle(); clonedCircle.setRadius(oldCircle.getRadius()); clonedCircle.setFill(oldCircle.getFill()); clonedCircle.setStroke(oldCircle.getStroke()); clonedCircle.setStrokeWidth(oldCircle.getStrokeWidth()); clonedNode = clonedCircle; } else if (oldNode.getClass() == Rectangle.class) { Rectangle oldRectangle = (Rectangle)oldNode; Rectangle clonedRectangle = new Rectangle(); clonedRectangle.setWidth(oldRectangle.getWidth()); clonedRectangle.setHeight(oldRectangle.getHeight()); clonedRectangle.setFill(oldRectangle.getFill()); clonedRectangle.setStroke(oldRectangle.getStroke()); clonedRectangle.setStrokeWidth(oldRectangle.getStrokeWidth()); clonedNode = clonedRectangle; } else if (oldNode.getClass() == Polygon.class) { Polygon oldTriangle = (Polygon)oldNode; Polygon clonedTriangle = new Polygon(); LinkedList<Double> clonedPoints = new LinkedList<Double>(oldTriangle.getPoints()); clonedTriangle.getPoints().setAll(clonedPoints); clonedTriangle.setFill(oldTriangle.getFill()); clonedTriangle.setStroke(oldTriangle.getStroke()); clonedTriangle.setStrokeWidth(oldTriangle.getStrokeWidth()); clonedNode = clonedTriangle; } else if (oldNode.getClass() == Line.class) { Line oldLine = (Line)oldNode; Line clonedLine = new Line(); clonedLine.setStartX(oldLine.getStartX()); clonedLine.setStartY(oldLine.getStartY()); clonedLine.setEndX(oldLine.getEndX()); clonedLine.setEndY(oldLine.getEndY()); clonedLine.setStroke(oldLine.getStroke()); clonedLine.setStrokeWidth(oldLine.getStrokeWidth()); clonedNode = clonedLine; } else if (oldNode.getClass() == Text.class) { Text oldText = (Text)oldNode; Text clonedText = new Text(); clonedText.setFont(oldText.getFont()); clonedText.setText(oldText.getText()); clonedText.setFill(oldText.getFill()); clonedNode = clonedText; } else if (oldNode.getClass() == Group.class) { Group oldGroup = (Group)oldNode; Group clonedGroup = new Group(); for (int i = 0; i < oldGroup.getChildren().size(); i++) clonedGroup.getChildren().add(NodeClone(oldGroup.getChildren().get(i))); clonedNode = clonedGroup; } else { throw new java.lang.Error("ERROR: Attempting to clone an unrecognized JavaFX oldNode type!"); } clonedNode.setTranslateX(oldNode.getTranslateX()); clonedNode.setTranslateY(oldNode.getTranslateY()); clonedNode.setLayoutX(oldNode.getLayoutX()); clonedNode.setLayoutY(oldNode.getLayoutY()); clonedNode.setRotate(oldNode.getRotate()); return clonedNode; }
我的问题是:直接通过Node的getter返回结果调用setter时,属性值是按值传递还是引用传递?换句话说,这个函数返回的克隆Node的属性是否真正独立,还是仍指向原Node的属性?我自己测试后发现函数似乎能生成完全独立的Node,但希望在完全采用该克隆方案前得到验证。
解答
首先明确:Java里所有参数传递都是值传递,但对于引用类型,传递的是对象引用的副本。克隆后属性是否独立,核心要看属性本身的类型是不可变还是可变:
基本数据类型/不可变对象
- 像
radius、width、height、strokeWidth、translateX这类数值属性,getter返回的是Double(不可变包装类),setter接收后,克隆对象的属性会持有这个不可变对象的引用。因为不可变对象无法被修改,后续修改原对象的这类属性(比如oldCircle.setRadius(20))只会让原对象指向新的Double实例,不会影响克隆体的属性值。 Text的text是String(不可变),Font也是不可变类型,所以克隆体的这两个属性和原对象共享引用,但因为不可变,不会出现一方修改影响另一方的情况。
- 像
可变对象(含集合)
- 填充/描边(
Paint类型):JavaFX标准库中的Paint实现(比如Color、LinearGradient)都是不可变的。所以getFill()/getStroke()返回的引用,set到克隆对象后,虽然是同一个引用,但因为不可变,修改原对象的填充/描边只会让原对象指向新的Paint实例,克隆体的填充/描边不会受影响。如果是自定义的可变Paint实现,才需要额外做深度克隆,但日常场景很少用到。 - Polygon的
points集合:你在代码里已经做了正确处理——通过new LinkedList<>(oldTriangle.getPoints())创建了新的列表,再用setAll()赋值给克隆体,所以克隆体的points是完全独立的集合,修改原对象的points不会影响克隆体,反之亦然。
- 填充/描边(
Group的子节点
- 代码里递归调用
NodeClone处理每个子节点,所以克隆后的Group的子树都是全新的Node实例,和原Group的子节点没有引用关联,完全独立。
- 代码里递归调用
总结
你编写的克隆函数在当前处理的Node类型和属性范围内,生成的克隆对象是完全独立的,不会和原Node共享可变状态。只要不使用自定义的可变属性对象,这个方案可以安全使用。
内容的提问来源于stack exchange,提问作者Runsva
相关产品推荐
相关产品推荐

