我才刚刚开始学习Java中的OOP编程。我已经用C++进行了一些编程,而Java中我最想念的一件事就是可以返回多个值。确实,C++函数仅严格返回一个变量,但我们可以使用按引用参数返回更多变量。相反,在Java中,我们无法做到这一点,至少对于原始类型而言,我们无法做到。
我想到的解决方案是创建一个类,该类将要返回的变量分组并返回该类的实例。例如,我需要在数组中查找一个对象,并且想要返回一个布尔值(是否找到)和一个索引。我知道只要未发现任何问题,我就可以将索引设置为-1,但我认为反之则更清楚。
事实是,有人告诉我,我对Java的了解比我知道的要多,我不应该为返回多个值(即使它们是相关的)而创建类。他告诉类永远不要将C类结构用作组元素。他还说方法不应返回非原始对象,而应从外部接收对象并仅对其进行修改。以下哪项是对的?
最佳答案
我不应该出于返回多个值的目的而创建类
类绝不能仅用作对元素进行分组的C++结构。
方法不应该返回非原始对象,它们应该从外部接收对象并仅对其进行修改
对于上述任何陈述,绝对不是这种情况。数据对象很有用,实际上,从包含大量逻辑的类中分离纯数据是一种很好的做法。
在Java中,最接近结构的是POJO(普通的Java对象),在其他语言中通常称为数据类。这些类只是数据的分组。根据经验,POJO应该只包含基元,简单类型(字符串,盒装基元等),简单容器(映射,数组,列表等)或其他POJO类。基本上是可以轻松序列化的类。
想要将两个,三个或n
对象配对在一起是很常见的。有时数据足够重要,可以保证拥有一个全新的类,而在其他情况下则不然。在这些情况下,程序员经常使用Pair
或Tuple
类。这是一个两元素通用元组的快速示例。
public class Tuple2<T,U>{
private final T first;
private final U second;
public Tuple2(T first, U second) {
this.first = first;
this.second = second;
}
public T getFirst() { return first; }
public U getSecond() { return second; }
}
使用元组作为方法签名的一部分的类可能如下所示:public interface Container<T> {
...
public Tuple2<Boolean, Integer> search(T key);
}
创建这样的数据类的一个缺点是,为了提高生活质量,我们必须实现toString
,hashCode
,equals
getter,setters,构造函数等。对于每个不同大小的元组,您都必须创建一个新类(Tuple2
, Tuple3
,Tuple4
等)。创建所有这些方法会在我们的应用程序中引入一些细微的错误。由于这些原因,开发人员通常会避免创建数据类。像龙目岛这样的图书馆对于克服这些挑战非常有帮助。使用上面列出的所有方法,我们对
Tuple2
的定义可以写成:@Data
public class Tuple2<T,U>{
private final T first;
private final U second;
}
这也使创建自定义响应类非常容易。使用自定义类可以避免使用泛型自动装箱,并大大提高了可读性。例如:@Data
public class SearchResult {
private final boolean found;
private final int index;
}
...
public interface Container<T> {
...
public SearchResult search(T key);
}
方法应该从外部接收对象并且只能对其进行修改
这是个坏建议。围绕不变性设计数据要好得多。从Effective Java 2nd Edition, p75
不可变对象是简单的。一个不可变的对象可以恰好处于一种状态,即创建时的状态。如果确保所有构造函数都建立类不变式,那么可以保证这些不变式在所有时间内都保持为真,而您或使用该类的程序员则无需再作任何努力。另一方面,可变对象可以具有任意复杂的状态空间。如果文档没有提供对由mutator方法执行的状态转换的精确描述,则可能难以或不可能可靠地使用可变类。
不可变对象本质上是线程安全的;他们不需要同步。它们不能被同时访问它们的多个线程破坏。这无疑是实现线程安全的最简单方法。实际上,没有线程能够观察到另一个线程对不可变对象的任何影响。因此,不可变对象可以自由共享。