Object的clone方法非常棘手.它基于现场副本,而且是"超语言".它创建一个对象而不调用构造函数.无法保证它保留构造函数建立的不变量.多年来,在Sun内外都存在许多错误,这源于这样一个事实,即如果你只是反复调用super.clone直到克隆了一个对象,那么你就拥有了一个浅层的对象副本.
Joshua Bloch用语言外的意思是什么意思?
从Effective Java第2版,第17项:
对于每个公共或受保护的方法或构造函数,文档必须指出方法或构造函数调用哪些可覆盖的方法
后来在同一个项目中说:
构造函数不得直接或间接调用可覆盖的方法.
这两个陈述不矛盾,还是我错过了什么?
"有效Java"中提到了"通过接口引用对象"是一种很好的做法.所以例如我更喜欢
List<String> al = new ArrayList<String>();
Run Code Online (Sandbox Code Playgroud)
过度
ArrayList<String> al = new ArrayList<String>();
Run Code Online (Sandbox Code Playgroud)
在我的代码中.一件令人讨厌的事情是,如果我输入ArrayList<String> al = new然后按下Eclipse中的Ctrl + Space,我会得到一个ArrayList<String>()propostal.但是如果我输入List al = new然后点击Ctrl + Space我将只得到propostal来定义匿名内部类,但不是propostal,例如new ArrayList<String>(),99%的情况,或者例如new Vector<String>().
问题:有没有办法将子类作为泛型类型的propostals?
我有一个关于"Effective Java"中涵盖的"Builder Pattern"的问题.我们需要一种.build()方法来正确实现模式吗?例如,假设我们有以下类:
public class CoffeeDrink {
private int numEspressoShots;
private short milkType;
private boolean withWhip;
private CoffeeDrink() {
}
public static CoffeeDrink buildNewDrink() {
return new CoffeeDrink();
}
public CoffeeDrink withEspresso(int n) {
this.numEspressoShots = n;
return this;
}
public CoffeeDrink withMilkType(shot t) {
this.milkType = t;
return this;
}
public CoffeeDrink withWhip() {
this.withWhip = true;
return this;
}
}
Run Code Online (Sandbox Code Playgroud)
然后我们如何使用它:
CoffeeDrink c = CoffeeDrink.buildNewDrink()
.withEspresso(2)
.withMilkType(2)
.withWhip();
Run Code Online (Sandbox Code Playgroud)
如果我没有静态内部Builder类,这仍然有效吗?我猜其中一个优点是,CoffeeDrink在.build()调用该方法之前,它不会创建新对象,但我仍在创建一个Builder …
我正在阅读Effective Java 3并注意到第43项中的这段代码:"首选方法引用lambdas":
TreeMap<K,V>::new
Run Code Online (Sandbox Code Playgroud)
注意类型参数.我一直都这么做:
TreeMap::new
Run Code Online (Sandbox Code Playgroud)
我使用Intellij并且从未收到有关此更改或任何更改建议的警告.事实上,当我让IDE将上面的方法引用更改为lambda时,它将其转换为
() -> new TreeMap<Integer, Integer>()
Run Code Online (Sandbox Code Playgroud)
包含类型参数的价值是什么?编译器不能根据变量的类型参数推断它吗?根据IDE如何将方法引用转换为lambda,它似乎可以.
我一直在阅读Effective Java,我对第一个项目"使用静态工厂方法而不是构造函数"与TDD和依赖注入有关.
该项目表示您应该避免使用public/protected/default构造函数并使用静态工厂公开它.我同意与使用静态工厂相关的所有优点,例如工厂可以有名称,你可以返回子类型,你可以减少冗长等等.但是,我认为缺点是约书亚错过了TDD,因为你的代码中有静态工厂将导致紧密耦合和你不能用它来模拟这个类.我们将无法模拟将拥有静态工厂的类.因此,它阻碍了测试驱动的开发.
第二点,我认为他错过了在今天的企业开发中大多数应用程序使用一个或另一个依赖注入容器.所以,当我们可以使用DI注入依赖项时,为什么要使用它呢?
请解释它如何应用于今天的Java企业开发,包括DI和TDD.
考虑Effective Java泛型章节中UnaryFunction定义的接口.
public interface UnaryFunction<T> {
T apply(T arg);
}
Run Code Online (Sandbox Code Playgroud)
和以下代码返回 UnaryFunction
// Generic singleton factory pattern
private static UnaryFunction<Object> IDENTITY_FUNCTION = new UnaryFunction<Object>() {
public Object apply(Object arg) { return arg; }
};
// IDENTITY_FUNCTION is stateless and its type parameter is
// unbounded so it's safe to share one instance across all types.
@SuppressWarnings("unchecked")
public static <T> UnaryFunction<T> identityFunction() {
return (UnaryFunction<T>) IDENTITY_FUNCTION;
}
Run Code Online (Sandbox Code Playgroud)
为什么演员IDENTITY_FUNCTION要(UnaryFunction<T>)安全?
这本书说的是我要问的问题,但我不能遵循这里的逻辑.我们在哪里调用apply执行身份操作的函数?我很困惑,因为它是返回传递给它的同一个对象而不修改任何东西的函数.
IDENTITY_FUNCTION
(UnaryFunction<T>) …
我有以下来自Joshua Bloch的有效Java代码(第9章,第3章,第49页)
如果类是不可变的并且计算哈希代码的成本很高,您可以考虑在对象中缓存哈希代码,而不是每次请求时重新计算它.如果您认为此类型的大多数对象将用作哈希键,则应在创建实例时计算哈希码.否则,您可能会在第一次调用hashCode时选择懒惰地初始化它(Item 71).目前尚不清楚我们的PhoneNumber类是否值得这样做,只是为了向您展示它是如何完成的:
// Lazily initialized, cached hashCode
private volatile int hashCode; // (See Item 71)
@Override public int hashCode() {
int result = hashCode;
if (result == 0) {
result = 17;
result = 31 * result + areaCode;
result = 31 * result + prefix;
result = 31 * result + lineNumber;
hashCode = result;
}
return result;
}
Run Code Online (Sandbox Code Playgroud)
我的问题是如何缓存(记住hashCode)在这里工作.第一次hashCode()调用方法,没有hashCode将其分配给结果.这个缓存如何工作的简要解释将是伟大的.谢谢
请查看Joshua Bloch的Effective Java 链接.
在第二段中,作者说:
该类是私有的或包私有的,并且您确定它的equals方法永远不会被调用.可以说,在这些情况下应该覆盖该
equals方法,以防它被意外调用:Run Code Online (Sandbox Code Playgroud)@Override public boolean equals(Object o) { throw new AssertionError(); // Method is never called }
请解释一下.我对作者使用术语私有类感到困惑,并且当我们确定不会被调用时,为什么需要重写equals方法.
在Effective Java(第2版)的第2项中,作者提到了以下关于在使用Builders时对参数施加不变量的问题:
在将参数从构建器复制到对象之后检查它们并在对象字段而不是构建器字段(项目39)上检查它们是至关重要的.如果违反了任何不变量,则构建方法应抛出IllegalStateException(Item 60).
这是否意味着在构建方法创建目标对象之后,应将其传递给验证例程以进行任何所需的验证?
另外,有人可以解释一下这背后的原因吗?