Utils class 中的谓词应该作为常量或静态方法提供?
Predicates in Utils class should be provided as constants or as static method?
最近,我一直在使用谓词和番石榴实用程序。我创建了一个 Utils.class 来存储我在代码的不同部分使用的一些谓词。因此这个问题出现了,我们(我和我的同事)没有就此达成一致。
将谓词放入实用程序 class 中的正确方法或 "good practice way" 是什么?作为用大写字母或静态方法定义它的常量?。下面,我写一个例子:
public final class Utils {
public static final Predicate<Element> IS_SPECIAL = new Predicate<Element>() {
@Override
public boolean apply(Element elem) {
return elem.special;
}
};
public static Predicate<Element> isSpecial() {
return new Predicate<Element>() {
@Override
public boolean apply(Element elem) {
return elem.special;
}}
顺便说一句,guava 提供了一些预定义谓词,并将它们作为 returns 谓词的方法提供,但其他库也提供相同的常量。
isSpecial()
每次调用时都会创建谓词的新实例。因此我会选择 IS_SPECIAL
。
我可能会这样做...
public final class Utils {
private static final Predicate<Element> IS_SPECIAL = new Predicate<Element>() {
@Override
public boolean apply(Element elem) {
return elem.special;
}
};
public static Predicate<Element> isSpecial() {
return IS_SPECIAL;
}
}
这样您就可以将其作为方法调用,但不会每次都实例化谓词...
正如其他答案所指出的,这两种解决方案之间存在差异:静态常量实例化单个对象,但该对象永远不会被垃圾回收,而方法每次调用时都会实例化一个新对象。不过,性能上的差异可以忽略不计:谓词的内存占用量非常小,垃圾收集器擅长收集短期对象。
由于灵活性,我仍然会选择方法解决方案:如果需要,您可以将方法重构为 return 一个(私有)静态变量,而不会破坏任何旧代码。但是 public 静态变量仍然是 public 静态变量。
对此有两种看法:API和实施。
关于 API,使用方法的解决方案比其他方法 灵活得多。它允许在不关心代码的情况下更改实现,这意味着您的代码不会受到隐藏在方法后面的任何更改的影响。
关于实现,人们通常使用枚举。这里枚举相对于常量的优势很多:
- 它是线程安全的。如果您使用单例变量,如果某些事情处理不当,您仍然会遇到内存问题。枚举是语言的一部分,定义将强制一致地加载每个枚举常量。
- 正如 rinde 在评论中提到的,枚举是可序列化的。这意味着您可以保留谓词以供进一步参考。
- 枚举提供了一种子命名空间。如果要创建多个谓词,可以将它们都定义在同一个地方,这样很容易找到它们。
- 枚举不会用显式子 类 污染代码,您不会每隔几行就看到
new Predicate<Element>
。然而,这种枚举的性质使得在编译后的 类 中不可能有相同的枚举。所以在这里,纯粹是在源代码层面上做得更好。当然,这就是你想要的,因为你实际上是在编写源代码。
这是我将如何做的一个例子。 (是的,我什至会根据最近的 Java 默契将 Utils
重命名为 Elements
。
public final class Elements {
private static enum ElementPredicate implements Predicate<Element> {
SPECIAL {
@Override public boolean apply(Element e) { return e.special; }
}
}
public static Predicate<Element> isSpecial() {
return ElementPredicate.SPECIAL;
}
private Elements() {}
}
最后,请注意,如果您使用的是 Java 8,您应该坚持提供的 lambda 机制,正如 Louis Wasserman 在问题评论中提到的那样。但是由于 lambas 在您的示例中不可用,因为您不是从方法中而是直接从字段中检索特殊信息,因此不幸的是,这个建议是无效的。但是,如果您在 Element
上有一个 isSpecial()
方法,您就可以直接在代码中编写谓词,如下所示:
Stream<T> stream = ... ;
stream
.filter(Element::isSpecial)
最近,我一直在使用谓词和番石榴实用程序。我创建了一个 Utils.class 来存储我在代码的不同部分使用的一些谓词。因此这个问题出现了,我们(我和我的同事)没有就此达成一致。
将谓词放入实用程序 class 中的正确方法或 "good practice way" 是什么?作为用大写字母或静态方法定义它的常量?。下面,我写一个例子:
public final class Utils {
public static final Predicate<Element> IS_SPECIAL = new Predicate<Element>() {
@Override
public boolean apply(Element elem) {
return elem.special;
}
};
public static Predicate<Element> isSpecial() {
return new Predicate<Element>() {
@Override
public boolean apply(Element elem) {
return elem.special;
}}
顺便说一句,guava 提供了一些预定义谓词,并将它们作为 returns 谓词的方法提供,但其他库也提供相同的常量。
isSpecial()
每次调用时都会创建谓词的新实例。因此我会选择 IS_SPECIAL
。
我可能会这样做...
public final class Utils {
private static final Predicate<Element> IS_SPECIAL = new Predicate<Element>() {
@Override
public boolean apply(Element elem) {
return elem.special;
}
};
public static Predicate<Element> isSpecial() {
return IS_SPECIAL;
}
}
这样您就可以将其作为方法调用,但不会每次都实例化谓词...
正如其他答案所指出的,这两种解决方案之间存在差异:静态常量实例化单个对象,但该对象永远不会被垃圾回收,而方法每次调用时都会实例化一个新对象。不过,性能上的差异可以忽略不计:谓词的内存占用量非常小,垃圾收集器擅长收集短期对象。
由于灵活性,我仍然会选择方法解决方案:如果需要,您可以将方法重构为 return 一个(私有)静态变量,而不会破坏任何旧代码。但是 public 静态变量仍然是 public 静态变量。
对此有两种看法:API和实施。
关于 API,使用方法的解决方案比其他方法 灵活得多。它允许在不关心代码的情况下更改实现,这意味着您的代码不会受到隐藏在方法后面的任何更改的影响。
关于实现,人们通常使用枚举。这里枚举相对于常量的优势很多:
- 它是线程安全的。如果您使用单例变量,如果某些事情处理不当,您仍然会遇到内存问题。枚举是语言的一部分,定义将强制一致地加载每个枚举常量。
- 正如 rinde 在评论中提到的,枚举是可序列化的。这意味着您可以保留谓词以供进一步参考。
- 枚举提供了一种子命名空间。如果要创建多个谓词,可以将它们都定义在同一个地方,这样很容易找到它们。
- 枚举不会用显式子 类 污染代码,您不会每隔几行就看到
new Predicate<Element>
。然而,这种枚举的性质使得在编译后的 类 中不可能有相同的枚举。所以在这里,纯粹是在源代码层面上做得更好。当然,这就是你想要的,因为你实际上是在编写源代码。
这是我将如何做的一个例子。 (是的,我什至会根据最近的 Java 默契将 Utils
重命名为 Elements
。
public final class Elements {
private static enum ElementPredicate implements Predicate<Element> {
SPECIAL {
@Override public boolean apply(Element e) { return e.special; }
}
}
public static Predicate<Element> isSpecial() {
return ElementPredicate.SPECIAL;
}
private Elements() {}
}
最后,请注意,如果您使用的是 Java 8,您应该坚持提供的 lambda 机制,正如 Louis Wasserman 在问题评论中提到的那样。但是由于 lambas 在您的示例中不可用,因为您不是从方法中而是直接从字段中检索特殊信息,因此不幸的是,这个建议是无效的。但是,如果您在 Element
上有一个 isSpecial()
方法,您就可以直接在代码中编写谓词,如下所示:
Stream<T> stream = ... ;
stream
.filter(Element::isSpecial)