在有效Java中 的项目"项目22:偏爱非静态的静态成员类"Josh Bloch说:
非静态成员类的每个实例都与其包含类的封闭实例隐式关联.在非静态成员类的实例方法中,您可以使用限定的此构造调用封闭实例上的方法或获取对封闭实例的引用.
合格的这个结构是什么意思?
我有一个重载方法,分别接受Consumer和Function对象,并返回一个匹配相应的Consumer/Function的泛型类型.我认为这样会好,但是当我尝试使用lambda表达式调用任一方法时,我得到一个错误,指示对该方法的引用是不明确的.
基于我对JLS§15.12.2.1的阅读.确定可能适用的方法:似乎编译器应该知道我的带有void块的lambda与Consumer方法匹配,而带有返回类型的lambda与Function方法匹配.
我把以下无法编译的示例代码放在一起:
import java.util.function.Consumer;
import java.util.function.Function;
public class AmbiguityBug {
public static void main(String[] args) {
doStuff(getPattern(x -> System.out.println(x)));
doStuff(getPattern(x -> String.valueOf(x)));
}
static Pattern<String, String> getPattern(Function<String, String> function) {
return new Pattern<>(function);
}
static ConsumablePattern<String> getPattern(Consumer<String> consumer) {
return new ConsumablePattern<>(consumer);
}
static void doStuff(Pattern<String, String> pattern) {
String result = pattern.apply("Hello World");
System.out.println(result);
}
static void doStuff(ConsumablePattern<String> consumablePattern) {
consumablePattern.consume("Hello World");
}
public static class Pattern<T, R> {
private final Function<T, R> …Run Code Online (Sandbox Code Playgroud) 我想更好地理解当Java编译器遇到如下方法的调用时会发生什么.
<T extends AutoCloseable & Cloneable>
void printType(T... args) {
System.out.println(args.getClass().getComponentType().getSimpleName());
}
// printType() prints "AutoCloseable"
Run Code Online (Sandbox Code Playgroud)
我很清楚,<T extends AutoCloseable & Cloneable>在运行时没有类型,因此编译器可以做出最少的错误,并创建一个类型为两个边界接口之一的数组,丢弃另一个.
无论如何,如果切换接口的顺序,结果仍然是相同的.
<T extends Cloneable & AutoCloseable>
void printType(T... args) {
System.out.println(args.getClass().getComponentType().getSimpleName());
}
// printType() prints "AutoCloseable"
Run Code Online (Sandbox Code Playgroud)
这导致我做了一些调查,看看接口发生变化时会发生什么.在我看来,编译器使用某种严格的顺序规则来决定哪个接口是最重要的,并且接口在代码中出现的顺序不起作用.
<T extends AutoCloseable & Runnable> // "AutoCloseable"
Run Code Online (Sandbox Code Playgroud)
<T extends Runnable & AutoCloseable> // "AutoCloseable"
Run Code Online (Sandbox Code Playgroud)
<T extends AutoCloseable & Serializable> // "Serializable"
Run Code Online (Sandbox Code Playgroud)
<T extends Serializable & AutoCloseable> // "Serializable"
Run Code Online (Sandbox Code Playgroud)
<T extends SafeVarargs & Serializable> // "SafeVarargs"
Run Code Online (Sandbox Code Playgroud)
<T extends Serializable …Run Code Online (Sandbox Code Playgroud) 我最近在一次演讲中听到,写入volatile会触发线程写入的每个变量的内存屏障.这是真的正确吗?从JLS看来,似乎只有相关的变量才被刷新,而其他变量则没有.有人知道什么是正确的吗?能指出我在JLS的具体位置吗?
假设我有一个通用的class Generic<A extends BaseType>.
就Java语言规范而言,在以下两种类型声明之间是否存在显着差异?
Generic<?>
Generic<? extends BaseType>
Run Code Online (Sandbox Code Playgroud)
嵌套通配符怎么样?
List<Generic<?>>
List<Generic<? extends BaseType>>
Run Code Online (Sandbox Code Playgroud)
考虑到这一点,我认为这些是等价的.Generic指定type参数A具有BaseType上限.
因此,BaseType无论是否明确指定,通配符都应始终"自动"或"隐式"限制.
下面,我尝试调和我的直觉与JLS.
我找不到有关"隐式"边界的信息,所以我从查看子类型规则开始.
阅读有关子类型$ 4.10.2的JLS部分,它说:
给定泛型类型声明
C<F1,...,Fn>(n> 0),参数化类型的直接超类型C<T1,...,Tn>(其中Ti(1≤i≤n)是一个类型)是以下所有:
D<U1 ?,...,Uk ?>,其中D<U1,...,Uk>泛型类型是泛型类型的直接超类型C<T1,...,Tn>,θ是替换[F1:= T1,...,Fn:= Tn].
C<S1,...,Sn>,其中Si含有Ti(1≤i≤n)(§4.5.1).
(强调我的)
据我所知,"通配符"在JLS中不被视为"类型".所以这不适用于前两个,但它适用于这两个List例子.
相反,这应该适用:
给定泛型类型声明
C<F1,...,Fn>(n> 0),参数化类型的直接超类型(C<R1,...,Rn>其中Ri( 1≤i≤n)中的至少一个是通配符类型参数)是参数化类型的直接超类型C<X1,...,Xn>,这是结果将捕获转换应用于C<R1,...,Rn>(§5.1.10).
(强调我的)
将捕获转换$ 5.1.10应用于Generic<?>和Generic<? …
我正在使用Java Reflection API,并观察到具有可变参数列表的方法变得短暂.为什么这个以及transient关键字在这种情况下意味着什么?
来自Java Glossary,transient:
Java编程语言中的关键字,指示字段不是对象的序列化形式的一部分.当对象被序列化时,其瞬态字段的值不包括在串行表示中,而其非瞬态字段的值包括在内.
然而,这个定义并未说明方法.有任何想法吗?
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class Dummy {
public static void main(String[] args) {
for(Method m : Dummy.class.getDeclaredMethods()){
System.out.println(m.getName() + " --> "+Modifier.toString(m.getModifiers()));
}
}
public static void foo(int... args){}
}
Run Code Online (Sandbox Code Playgroud)
输出:
main --> public static
foo --> public static transient
Run Code Online (Sandbox Code Playgroud) 我一直在阅读Java语言规范第3版,并且发现了我认为规范和javac编译器实现之间的差异.Eclipse编译器中存在相同的差异.
第15.16节讨论了强制转换表达式.如果参数类型无法通过强制转换转换为强制类型,那么它应该是编译时错误(第5.5节):
如果根据转换转换规则(第5.5节),操作数的编译时类型永远不会转换为强制转换运算符指定的类型,那么这是一个编译时错误.否则,在运行时,通过将转换转换为强制转换运算符指定的类型来转换操作数值(如果需要).
第5.5节讨论了转换.它给出了允许的转换类型列表.列表中特别缺少的是"取消装箱转换,然后加宽/缩小原始转换".但是,javac编译器(以及Eclipse编译器)似乎确实允许确切的转换序列.例如:
long l = (long) Integer.valueOf(45);
Run Code Online (Sandbox Code Playgroud)
......编译得很好.(有问题的强制转换是强制转换long;参数是类型java.lang.Integer,因此转换需要拆箱int后跟扩展的原始转换).
同样地,根据JLS它不应该是可以从铸造byte到char,因为(根据5.1.4)需要加宽原语转换和一个基本收缩转换-然而,该铸造也由编译器允许的.
任何人都可以开导我吗?
编辑:自从问这个以来,我已经向Oracle 提交了一份错误报告.他们的反应是,这是"JLS中的一个小故障".
既然JDK 7开发人员预览已经完成,人们可能会认为现在是新JLS的时候了.毕竟,语言已经发生了变化,虽然很小.
我还没找到任何东西.何时可以使用新的JLS,从哪里可以获得它?
考虑以下一组表达式:
class T {{
/*1*/ Object o = T.super; // error: '.' expected
/*2*/ o.toString();
}}
Run Code Online (Sandbox Code Playgroud)
尝试编译这将失败的行/*1*/出现错误:
error: '.' expected
o = T.super;
^
Run Code Online (Sandbox Code Playgroud)
使用OpenJDK 1.8.0(Ubuntu)或Oracle JDK 1.8(Windows)时.
但是,Eclipse 4.5.0(Mars) 编译它没有任何错误,它导致:
class T {
T();
0 aload_0 [this]
1 invokespecial java.lang.Object() [8] // super()
4 aload_0 [this]
5 astore_1 [o] // o = T.super
7 invokevirtual java.lang.Object.toString() : java.lang.String [10]
10 pop // ^-- o.toString()
11 return
}
Run Code Online (Sandbox Code Playgroud)
从中您可以看到/*1*/ …
这段丑陋的代码确实可以编译但是会抛出NPE s == null
public static boolean isNullOrEmpty(String s)
{
return s != null ? s.isEmpty() : null;
}
Run Code Online (Sandbox Code Playgroud)
虽然这不(如预期):
public static boolean isNullOrEmpty(String s)
{
if(s != null)
return s.isEmpty();
else
return null;
}
Run Code Online (Sandbox Code Playgroud)
我知道它们都是明显错误的,但是当我在源代码中找到第一段代码时,我很惊讶它编译了.
编辑:这是Java 7中JLS的相关部分.我猜测第一个语句适用但粗体语句适用.
15.25条件运算符?:
[...]
条件表达式的类型确定如下:
[...]
[...]
java ×10
jls ×10
java-8 ×2
concurrency ×1
eclipse ×1
generics ×1
java-7 ×1
javac ×1
lambda ×1
overloading ×1
reflection ×1
super ×1
volatile ×1
wildcard ×1