如果我的IDE非常聪明,为什么我需要强制转换“ clone()”?


13

我在输入代码时,我的IDE(NetBeans)类型检查我Collections。但是,为什么我必须转换返回的对象Object.clone()?没关系 无害无犯规。但是我还是不明白。

是否在没有强制转换的情况下进行类型检查,Object.clone()否则无法返回的对象?该仿制药框架让我觉得IDE可以检查类型的“的右侧的对象引用= ”标记而无需进行转换,而我打字?我不明白

附录
我的用例只是我有一个私有Calendar字段pubdate。我打算写:

Calendar getPubdate() {
    return pubdate;
}

但是存在调用者可以修改我的pubdate的风险,因此我返回了一个副本:

Calendar getPubdate() {
    return (Calendar) pubdate.clone();
}

然后,我想知道为什么我需要铸造pubdate.clone()。方法签名在那里具有类型。NetBeans应该能够弄清楚这一点。和NetBeans的似乎在做关于类似的东西Collections


5
一个简短的(!)代码示例将很有帮助。
布朗

78
IDE与语言本身是分开的,因此,Netbeans的智能程度对Java语言的工作方式没有影响。
JacquesB '16

7
请注意,由于返回类型的协方差,建议返回MyObjectclone(),而不是Object-这消除这整个问题。进一步建议不要使用clone()(有效Java项目#11)。
蜘蛛鲍里斯(Boris)

您的问题重点是I还是“ cast“ clone()””?因为前者提出的问题实际上可能比后者提出的问题好得多。
user541686 '16

当我阅读问题标题时,我唯一想到的就是this.clone()程序员对象,尤其是在Tue发行后的周三晚上。抱歉,但我必须写此评论。.为什么智能IDE不能为我们修复所有错误大声笑
2016年

Answers:


53

为什么我必须强制转换返回的对象Object.clone()

因为它返回了Object

仿制药框架让我觉得IDE可以检查类型的“的右侧的对象引用= ”标记而无需进行转换,而我打字?我不明白

Object.clone 不是通用的。

如果在clone设计时已存在泛型,则它可能看起来像这样(使用F绑定多态):

interface Cloneable<T extends Cloneable<T>> {
  T clone();
}

如果Java具有MyType功能,则可能看起来像这样:

interface Cloneable {
  this clone();
}

但是,泛型在Object.clone设计之初就不存在,并且Java没有MyTypes,因此Object.clone我们必须使用的类型不安全的版本。


我只是意识到让我感到困惑的IDE功能是后台编译?我的意思是,这就是启用泛型进行代码完成和自动类型检查的原因?就个人而言,我宁愿将类型检查延迟到显式编译之前。但是我喜欢代码完成。
konishiki 2016年

1
@konishiki具有Java 8(在某种程度上是Java 7)类型推断,为了提供任何帮助,IDE需要推断类型。这需要部分编译。在类型推断之前,自动完成不需要编译。
蜘蛛鲍里斯(Boris)

@BoristheSpider非常感谢您的反馈!我当然需要做更多的测试/思考。非常感谢。
konishiki 2016年

你为什么需要通过original
Clashsoft

@Clashsoft:愚蠢的思想家,同时改编了明显的函数式编程版本。谢谢。
约尔格W¯¯米塔格

26

这不是任何IDE的功能,而是语言定义的功能。

IDE可以帮助您更有效地使用编程语言,而不会更改该语言的语义。这意味着编辑器帮助程序可能会在很明显您想要哪个类型时自动插入类型转换,但是它不能简单地破坏语言规则并假装未编译的代码是有效的。

编辑诚然,IDE可以捆绑其自己的编译器,并且实际上很多都可以做到这一点,例如,为了更好地将错误信息与更多的内部信息一起报告到部分分析树中。但是,让此内部编译器实现与官方SDK不同的语言语义将是一个非常糟糕的主意,因为这意味着开发环境中可以工作的代码在安装到生产环境中时可能会神秘地开始失败-产生的问题是,可调试的!


这就是发生的事情:我很确定代码完成是一种IDE功能(通过反射完成)。我正在谈论的自动类型检查发生在IDE中(通过后台编译)。我希望那是怎么回事。
konishiki

1
IDE 可以很容易地破坏语言规范的所有规则,并且无论如何都可以编译代码。例如,我知道NetBeans和Eclipse都使用自己的编译器。因此,在这种情况下,IDE和编译器实际上是同一件事。并不是说这是一个好主意,但是C编译器肯定会一直这样做。
MichaelS

@MichaelS C语言规范旨在允许不同实现之间的重大变化,以便不同的供应商可以在不同的可能方法之间进行选择并添加其他功能。这在规范中产生了各种歧义,这是允许这些变化所必需的。名称“ C”没有商标,因此没有人可以控制该术语的使用。Java规范旨在消除尽可能多的歧义,并且Java已注册了商标,商标持有人要求其只能在兼容的实现中使用。
Jules

无论如何,现代IDE都需要编译器。代码完成提示要求解析光标之前的代码。当然,泛型(或C ++模板)需要完整的编译器,而不仅仅是解析器。
MSalters '16

3

这是由于Object.clone方法的类型签名所致。类型签名指出该方法将返回Object类型的对象。

protected Object clone() throws CloneNotSupportedException

集合将使用所谓的泛型类型来自动替换强制类型。

因此,如果您有以下代码:

List<Integer> ints = Arrays.asList(1,2,3);
int x = ints.get(0);`

编译器将在幕后为您添加强制类型转换,因此代码实际上是:

List ints = Arrays.asList(1,2,3);
int x = (Integer)ints.get(0);

0

为了完整起见,从Java 5开始,允许使用协变返回类型。因此,您可以编写以下代码:

public MyObject implements Cloneable {
  @Override
  public MyObject clone() {
    try {
      return (MyObject)super.clone();
    } catch (CloneNotSupportedException e) {
      throw new AssertionError();
    }
  }
}

这样,以下代码是合法的:

MyObject original = new MyObject();
MyObject clone = original.clone();
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.