几天前我为STS安装了Sonarint,我想问一个关于Java:S3252规则的问题(“静态”基类成员不应通过派生类型访问)。
在我的代码中,出于某种原因(我不是该公司的架构师),许多程序都使用了包含i18n密钥的接口。其中一个接口被称为regei18n
,并扩展了CommonI18n
。当我需要来自CommonI18n
的密钥时,如果类涉及“Regie”,例如regei18n,我会使用
,而不是regei18n
。按键_ACTION _EXECUTECommonI18n。按键_操作_执行
。在这种情况下,不遵守S3252规则。
在我看来,使用regei18n
(孩子)有助于防止开发人员在某一天,当有人决定用regei18n
的另一个值覆盖KEY\u ACTION\u EXECUTE
时重新编写一些代码。该规则也适用于方法(事实上,所有可访问的成员),请不要关注我们在示例中使用i18n密钥的事实。
你认为S3252的优点和缺点是什么?
这是危险地接近超出安全条例范围的“意见”。因此,在这个答案中没有太多的意见——只是S3252所基于的客观基础。
Java通常被称为“面向对象”,因此,它的大多数概念都是用OO来解释的——而一般Java程序员的直觉大概是面向对象的。
在这种情况下,这是一个问题:静态
东西不“做”OO。完全没有。-例如,动态分派(如果调用someAnimalRef.makeNoise()
,如果ref结果是引用Dog的实例,那么实际上会调用Dog类的makeNoise()
方法,即使变量someAnimalRef
是键入的Animal
)-根本不适用于static
。
换句话说,考虑到这是静态的东西,javac
决定使用哪个常量(对于返场,如果所说的常量是编译时间常量——即字符串或数字字面量,或者只能使用仅涉及文字量的内置数学(没有库)来导出——那么这个变量的任何引用都直接被常量替换)。
这可能令人困惑。按照您编写它的方式,您仍然会得到某种“继承”——但这是编译时继承。具体来说,如果您有(将这3个存储在单独的文件中):
interface CommonI18n {
String KEY_ACTION_EXECUTE = "Execute";
}
interface RegieI18N extends CommonI18n {
}
class Test {
public static void main(String[] args) {
System.out.println(RegieI18N.KEY_ACTION_EXECUTE);
}
}
并运行:
javac *.java; java Test
显然,它打印Execute
。如果您编辑RealeI18N. java
并重新编译它:
interface RegieI18N extends CommonI18n {
String KEY_ACTION_EXECUTE = "Uitvoeren";
}
// and then on the command line:
javac RegieI18N.java
java Test
它仍然打印Execute
,尽管您的意图显然是它现在应该打印Uitvoeren
。这有两个不同的原因(任何一个都足够):[A]静态的东西100%由javac
解决,所以除非您重新编译Test.java
,否则它永远不会工作,像这样的[B]常量是内联的。
这就是警告背后的潜在想法:这是令人困惑的,即使不是,这也会阻碍你未来的灵活性。除非重新编译整个应用程序,否则你不能引入/更改任何国际化的东西。考虑到一些“增量编译”工具不会重新编译测试,如果Test.class
比Test.java
更新,这是一件大事。
简单-不要使用常量。如果想要“在子类型中重写”功能,请不要使用static
。Java内置了i18n功能,您可以将数据放在属性文件中,然后将其与类文件一起发送(如果需要,可以放在JAR中)。这种基于接口的策略似乎基本上没有任何好处(我猜从技术上讲,它的性能更高,但我怀疑你是否能够衡量它,考虑到i18n有点像是“用于人眼的字符串”,而“用于人眼的字符串”有点像是暗示:CPU不会成为瓶颈,你可以完全忽略它),在这里,业绩的上升似乎是对前景的一种可笑的疯狂误解。为此担心性能是完全错误的。最有可能的是——当然,每一条规则都有例外。
如果你不想使用java的东西,那么一个好的开始就是调用一个方法(如果你真的需要的话是静态的)来获取这些字符串;你不想写regei18n。按_ACTION _EXECUTE
,您需要编写类似于regei18n的内容。getKeyActionExecute(),甚至是regei18n。按键_ACTION _EXECUTE()
(注意后面的括号)。如果必须,可以使用code gen创建这些方法。现在,您可以灵活地在这些应用程序中编写自己喜欢的任何内容,JVM将永远不会内联或以其他方式消除这样的调用。如果以后您决定希望该方法返回其他内容,并且希望通过重新编译regei18n来部署它,那么现在就可以了。
当然,你只是在糟糕地重新发明轮子,但这消除了S3252试图解决的主要问题。
除另有说明外,以下所有标准参考均指N4861(2020年3月布拉格后工作草案/C++20 DIS)。 如果基类是可访问的,则可以隐式地将指向派生类的指针转换为指向该基类[...]的指针。 意味着以下示例格式良好: 因为是上(+)的可访问基类。 [...] /5.3作为的成员受到保护,出现在类的成员或友类中,或者出现在从派生的类的成员中,其中作为的成员是公共的、私有的或受保护的,或者 /5.4存在一
考虑以下代码: GCC v6.1编译它,叮当声3.8拒绝它,错误如下: 2:错误:没有成员名为'foo'在'U' struct S{静态constexpr int bar=T::foo;}; 哪个编译器是对的? 会不会是因为在我们尝试在中使用它时不是一个完整的类型? 在这种情况下,它应该被认为是GCC的错误,但我想知道我是否正确之前在错误跟踪器上搜索/打开问题… 编辑 与此同时,我已经向GCC打开
问题内容: 我有一些用Java定义的类,类似于下面的代码。我正在尝试通过派生的Java类进行访问,这在Java中是允许的,但在kotlin中是不允许的。 有没有一种方法可以通过派生类访问字段? 问题答案: 在Kotlin中,嵌套类型和伴随对象不会自动继承。 此行为并非特定于Java,您可以仅在Kotlin中重现相同的行为: 因此,您必须显式地使用基类对嵌套类进行限定。 为了避免Java中与通过派生
我创建了Angular2 Typescript项目。我有很多表格,所以我想有一个类似基本组件的东西。 这是我的基本组件: 现在有我的子组件。我想从http获取所有项目,然后将其分配给基类 知道如何从订阅方法访问基字段吗?
下面是一个人为的例子:(实际代码中使用的命名方案太令人困惑) 我有一个班主任,他是第三方图书馆的一部分,不可更改。我也有一些课程延伸到父亲;比如儿子。祖父有一个类似这样的构造函数: 这个构造函数中实际发生的事情并不重要。重要的是,它所做的任何事情都必须由所有派生类完成,因此对的调用是每个派生类构造函数的第一行。这里的含义是,祖父的所有后代的构造函数,无论距离有多远,都必须始终调用super(或调用
问题内容: 我有一些用Java编写的代码。对于新课程,我计划在Scala中编写。我在访问基类的受保护静态成员时遇到问题。这是示例代码: Java代码: 标量代码: 有什么建议吗?我如何在不修改现有基类的情况下解决这个问题 问题答案: 这在Scala中是不可能的。由于Scala没有记号,您不能访问父类的成员。这是一个已知的限制。 解决方法是执行以下操作: 现在,您可以改为从这个新类继承,并通过get