使用strictfp关键字实现/扩展接口/类的行为

18

JLS strictfp接口指定:

strictfp修饰符的效果是使接口声明中的所有float或double表达式都显式地FP-strict(§15.4)。

这意味着在接口中声明的所有嵌套类型都是隐式strictfp的。

JLS strictfp类

strictfp修饰符的效果是使接口声明中的所有float或double表达式都显式地FP-strict(§15.4)。

这意味着在接口中声明的所有方法和所有嵌套类型都是隐式strictfp的。

从这两段文字中并没有说明使用strictfp修饰符声明的接口/类在实现/扩展时的行为。

在搜索后,我找到了一个关于使用strictfp关键字的好解释使用strictfp修饰符以实现跨平台浮点计算一致性,它指出:

严格行为不会被扩展FP-strict超类的子类继承。覆盖方法可以独立选择在被覆盖的方法不是FP-strict时成为FP-strict,反之亦然。

并且它补充说: enter image description here 我测试了使用strictfp关键字声明的类进行扩展时的行为,结果是正确的: strictfp行为不会被扩展该类的类继承,但问题在于当实现使用strictfp关键字声明的接口时,情况并非如此: strictfp行为不会被实现该接口的类继承。

有没有人能够解释一下在实现/扩展一个使用 strictfp 修饰符声明的接口/类时,strictfp 的正确行为是什么?


我测试了在扩展使用strictfp关键字声明的类时strictfp关键字的行为,结果是正确的:继承该类的类不会继承strictfp行为。问题是-你是如何测试的?我假设这是正确的,因为(根据官方文档)整个类不会是strictfp-只有接口中的方法。无论如何-你是如何进行测试的? - Piotr Reszke
听起来对我来说这部分有问题:“All code in any class that implements the interface”。无论如何,这种继承是发生在编译时间还是加载时间?在其他情况下,该关键字严格适用于一个.java源文件,并且其影响仅限于一个或多个产品的.class文件中。 - Teemu Ilmonen
@PiotrR 我所说的测试是指我执行了多个随机示例,涵盖了使用strictfp与接口、方法、构造函数和类的多种情况,并借助javap -c命令调查了生成代码的内容,以查看strictfp的行为是否被实现和扩展类继承。 - Naruto Biju Mode
@TeemuIlmonen 是的,我认为这是正确的,因为在实现使用 strictfp 关键字声明的接口的类中找不到 strictfp - Naruto Biju Mode
2个回答

9
以下是我进行的实验来调查你的问题。下面的代码使用反射API在各种情况下检查是否声明了strictfp
结论:
  1. strictfp接口中声明的抽象方法在实现该接口的类中将不是严格的
  2. strictfp接口中声明的默认方法在实现该接口的类中将是严格的
  3. 实现strictfp接口的类中的方法不会自动成为严格的
  4. strictfp接口的所有内部类中声明的方法都将具有stricfp修饰符
总之,如果在接口上声明了strictfp,则所有非抽象代码 - 默认方法、带有方法的内部类 - 都自动成为strictfp
请注意,strictfp修饰符不适用于抽象方法。
import java.lang.reflect.Modifier;

strictfp interface StrictInterface {

    void someInterfaceMethod();

    default void someInterfaceDefaultMethod() {}

    class InnerTest {
        public static void innerMethod() {}
    }
}

class Impl implements StrictInterface {
    @Override
    public void someInterfaceMethod() {}

    public strictfp void someClassMethod() {}

    public void someClassMethod2() {}
}

public class Test {
    public static void main(String argv[]) {

        checkModifiers(Impl.class, "someInterfaceMethod");
        checkModifiers(Impl.class, "someClassMethod");
        checkModifiers(Impl.class, "someClassMethod2");

        checkModifiers(Impl.class.getInterfaces()[0], "someInterfaceDefaultMethod");
        checkModifiers(StrictInterface.InnerTest.class, "innerMethod");
    }
    public static void checkModifiers(Class clazz, String m) {
        try {
            int mod = clazz.getDeclaredMethod(m, new Class[0]).getModifiers();
            String res = m + " modifiers: " + Modifier.toString(mod);
            System.out.println(res);
        } catch (Exception e) {
            e.printStackTrace(System.out);
        }
    }
}

程序的输出结果:(在OSX上使用jdk1.8.0_91.jdk)
someInterfaceMethod modifiers: public
someClassMethod modifiers: public strictfp
someClassMethod2 modifiers: public
someInterfaceDefaultMethod modifiers: public strictfp
innerMethod modifiers: public static strictfp

谢谢,你的代码使得调查生成的代码并检查是否继承了 strictfp 行为变得更加容易。 - Naruto Biju Mode

1

JLS §15.4非常清楚地说明了哪些表达式是FP-strict,哪些不是。

如果一个类、接口或方法X被声明为strictfp,则X以及X内的任何类、接口、方法、构造函数、实例初始化程序、静态初始化程序或变量初始化程序都被称为FP-strict。

由此可知,只有当表达式不是常量表达式且未出现在具有strictfp修饰符的任何声明中时,该表达式才不是FP-strict

这里的关键字是声明。如果一个类声明中没有strictfp修饰符,则该类内的表达式将不是FP-strict,无论该类实现了什么接口。

这符合您的观察结果。从常识上讲,这也是合理的;否则,将无法从类的任何成员(包括新引入的成员)中“重置”FP严格性。查看javac或HotSpot JVM源代码,您不会找到任何strictfp继承的迹象。

2
对我来说,这个定义并不是很直观。尽管它实现了strictfp接口,我并没有预料到整个类都会是strictfp的。但我不知道在实现类中,声明在接口中的抽象方法是否也会是strictfp的。JLS 15.4中的措辞可能需要改进。 - Piotr Reszke
现在很清楚了:https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.4 - Dávid Horváth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接