为什么java.util.Scanner类被声明为“final”?

6
我使用Scanner类来读取多个类似的文件,我想扩展它以确保它们都使用相同的分隔符,并且我还可以添加方法,例如对于它们都有效的skipUntilYouFind(String thisHere)
我可以制作一个实用程序类来包含它们,或将Scanner类嵌入另一个类中作为变量,但这更加繁琐。
我发现一些原因来声明一个类是final的,但为什么在这里要这样做呢?

如果这与性能有关,理论上是否可能制作一个非性能扫描器,它是可扩展的?我的逻辑认为应该可以。 - Roalt
3个回答

5

可能是因为扩展它并覆盖其中的一些方法可能会破坏它。而使其更容易重写方法将暴露太多内部工作原理,因此如果他们决定在将来更改这些内容(出于性能或其他原因),那么更改类将更加困难,而不会破坏所有扩展它的类。

例如,考虑以下方法:

public boolean nextBoolean()  {
    clearCaches();
    return Boolean.parseBoolean(next(boolPattern()));
}

假设您想重写此代码,因为您想使“ awesome”评估为“ true”布尔值(出于任何原因)。如果您覆盖它,就不能调用super.nextBoolean(),因为这会使用默认逻辑消耗下一个标记。但是,如果您不调用super.nextBoolean(),则不会调用clearCaches(),这可能会破坏其他未被覆盖的方法。您无法调用clearCaches(),因为它是私有的。如果它们将其设置为受保护状态,但随后发现它会导致性能问题,并希望使用不再清除缓存的新实现,则可能会破坏您覆盖的实现,后者仍将调用该实现。

所以基本上这是为了让他们可以轻松更改类内部的隐藏部分,这些部分相当复杂,并保护您免受制作错误的子类(或易于破坏的类)的影响。


这就解释了为什么函数nextBoolean应该定义为'final public boolean nextBoolean()',而不是整个类? - Roalt
是的,但你可以有一个没有clearCaches()方法的方法,并且它没有声明为final,但他们意识到他们也需要在那里添加clearCaches(),但现在他们不能声明它为final(因为这会破坏扩展它的类)。而且很多(如果不是所有)方法都非常依赖于隐藏的实现(很多方法从一开始就必须是final的)。声明整个内容为final更容易,也更有意义(因为应该是final的部分在更改内部实现时可能会发生变化)。 - Andrei Fierbinteanu
2
按照这个逻辑,一切都应该是 final 类。我真的不相信这是原因。 - corsiKa

1

我想这是出于安全原因。这个类读取用户输入,所以有恶意的人可以扩展它,修改它的行为,这样你就会受到影响。如果它是final的,对于坏人来说就不那么容易了,因为如果他制作自己的Scanner类型(而不是java.util.Scanner),多态的原则就会被破坏。坏人可以聪明到编写一个机器人/脚本,在远程服务器上自动执行此操作...... 他甚至可以通过已编译应用程序中的动态类加载来实现。


0

我认为你提供的链接已经解释了一切。

在你的情况下,似乎你应该更喜欢组合而不是继承。你正在创建一个具有一些预定义行为的实用程序,并且可以隐藏Scanner类的某些(或全部)细节。

我见过很多使用继承来改变行为的实现。最终结果通常是一个庞大的设计,在某些情况下,会破坏契约和/或破坏行为。


将其变成组合会增加代码的大小。这不是罪过,但您会得到my.scanner.next()(甚至是my.getScanner().next()而不是my.next())。我认为这样做没有那么美观,对吧?并且它更难以用“替换此处”的方式进行“替换那里”。 - Roalt
如果你使用了yourObject.scanner.next(),那么这就不是封装了吧?但是如果你使用了yourObject.process(input) : returnVal,那么就是封装了。 - Eran Harel

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