Java 我可以创建一个行为类似于基元类型的类吗?

Java 我可以创建一个行为类似于基元类型的类吗?,java,oop,Java,Oop,我创建了一个Point类来管理xy坐标(我知道一些java默认包已经有了一些行为类似的Point对象,我使用它作为一个懒惰的替代方案,这是一个示例,请不要介意它^^) 在使用它之前,我使用的是int原语类型 问题是,当传递一个类或一个基元类型作为方法的参数时,两者的作用并不相同,如果我在方法中更改基元变量的值,则原始变量不受影响,这与使用对象不同,因为它是作为参数传递的引用 我的问题是,由于我使用Point for math(下面的示例),我必须手动创建新对象来保存结果,而且我从未使用它在两个变

我创建了一个Point类来管理xy坐标(我知道一些java默认包已经有了一些行为类似的Point对象,我使用它作为一个懒惰的替代方案,这是一个示例,请不要介意它^^)

在使用它之前,我使用的是int原语类型

问题是,当传递一个类或一个基元类型作为方法的参数时,两者的作用并不相同,如果我在方法中更改基元变量的值,则原始变量不受影响,这与使用对象不同,因为它是作为参数传递的引用

我的问题是,由于我使用Point for math(下面的示例),我必须手动创建新对象来保存结果,而且我从未使用它在两个变量之间建立“链接”,就像我可以使用对象通常的行为一样

我的观点是:

public class Point {
    public int x;
    public int y;

    public Point(int nx, int ny) {
        this.x = nx;
        this.y = ny;
    }

    public Point() {
        this(0, 0);
    }
}
方法示例:

public Point getAdjacent(int x, int y, Direction dir) {
        Point pt = new Point(x, y);//basically, i would like to do that automatically
        switch (dir) {
        case Up:
            pt.y -= 1;
            break;
        case Down:
            pt.y += 1;
            break;
        case Right:
            pt.x -= 1;
            break;
        case Left:
            pt.x += 1;
            break;
        default:
            //do nothing
        }
        return pt;
    }
总而言之,是否有可能使Point的行为像一个基本类型?


编辑:我的意思是,当将其作为参数传递或执行point1=point2时,它会自动复制它。这个问题可能有点过于宽泛,因为人们可能会对“行为”一词的含义进行广泛的争论。但在某种程度上,我们可以解决这个问题:

最短的答案可能是:不,这是不可能的。

一个稍微复杂一点的答案可能是:不,现在还不可能让类(或者更准确地说:对象)像原语值一样工作。


答案很长:

有人努力实现一种行为,这种行为可能接近你想要实现的目标。此处的相关关键字是值类型

一些资源:

  • John Rose、Brian Goetz和Guy Steele提出的原始价值类型提案:
  • 价值对象的相关JEP:
  • 描述值类型的目标和当前状态的Wiki:
  • 相关邮件列表:
但是,当前版本的Java和JVM不支持这一点,而且可能还需要一段时间才能理清细节

在此之前,有一些可以想象的变通办法来实现预期目标

最简单的解决方案是您已经提出的解决方案:您总是返回一个新实例,而不是修改给定对象

您在问题中展示的示例可能不是最好的例子,因为您展示的方法
getnexture
实际上可能是一个
static
方法。它不使用以任何方式调用它的实例

然后,您可以始终确保每次修改都会收到一个新实例。否则,请想象以下代码片段:

Point a = new Point(1,2);
Point b = new Point(3,4);

a.add(b);

System.out.println(a); // What does this print?
根据
add
方法的实现,行为可能不清楚。如果是这样实施的:

public void add(Point other) {
    this.x += other.x;
    this.y += other.y;
}
然后修改点
a
,输出为
(4,6)

但如果是这样实施的话

public Point add(Point other) {
    return new Point(this.x+other.x, this.y+other.y);
}
然后
a
将保持不受影响,并且输出仍然是
(1,2)

一般来说,使点不可变基本上强制了这种编程风格。因此,您可以在类
final
中设置变量
x
y
,以便始终确保对象在创建后无法修改:

public class Point {

    // Note that these are FINAL:   
    private final int x; 
    private final int y;

    public Point(int nx, int ny) {
        this.x = nx;
        this.y = ny;
    }
    ...
}


对于像
类这样看似微不足道的东西,还有一些进一步的设计考虑(我在中提到了其中一些),但讨论它们超出了本答案的范围。

首先:确保在开关代码中使用break语句。否则,当case Up匹配时,开关块将“通过”并执行:

pt.y -= 1; 
pt.y += 1;
pt.x -= 1;
pt.x += 1;
(参考资料:)


根据问题,您确实可以使用Point类。以下代码将:
1.使用点作为输入。
2.将变量x和y从输入点提取到复制原语中。
3.更改复制的原语。
4.基于副本创建一个新点

这样,“旧”点就不会被触及

public Point getAdjacent(final Point point, final Direction dir) {
    int x = point.x;
    int y = point.y;
    switch (dir) {
        case Up:
            y -= 1;
            break;
        case Down:
            y += 1;
            break;
        case Right:
            x -= 1;
            break;
        case Left:
            x += 1;
            break;
        default:
            break;
    }
    return new Point(x, y);
}

在Java中,要做到这一点,必须使对象不可变,并且对其执行的任何操作都将返回对象的新实例。当将非原语类型传递到方法参数或以其他方式复制时,它是浅层复制,这意味着复制指向实际对象的指针,就好像同一对象有两个名称一样。您需要的是深度复制对象,在Point类中创建一个.clone()方法。它应该只返回一个与原始点具有相同x和y坐标的新点。所以基本上,你想通过一个点对象并从它返回相邻的点,对吗?为此,您应该使Point implement可克隆并实现/使用.clone()方法。或者使用一个复制构造函数。由于确定邻接的结果是一个
,因此您基本上必须创建一个新实例,因为您的方法同时处理X轴和Y轴(或者将其分为两种方法,分别处理每种情况,这似乎是倒退的)。你想避免创建一个新实例有什么原因吗?谢谢你对开关的更正,我调整了它以不分散主要问题的注意力。我不知道你的解决方案,让两个新的x和y变量最终在我试图删除它们的地方添加行:D。谢谢你的回答,确实这个方法是静态的(我认为把它移走是个好主意,以保持清楚,这是我的错误)很高兴知道我不想做一些毫无意义的事情:D