【问题标题】:How does relying on type inference affect code maintainability?依赖类型推断如何影响代码的可维护性?
【发布时间】:2012-04-15 20:53:11
【问题描述】:

虽然此问题中的示例使用 Visual Basic.NET 和 Java,但该主题可能也适用于实现类型推断的其他语言。

到目前为止,我只在 VB.NET 中使用 LINQ 时使用过类型推断。但是,我知道类型推断可以用于该语言的其他部分,如下所示:

Dim i = 1    'i is inferred to be an Integer
Dim s = "Hoi"    's is inferred to be a string

Dim Temperatures(10) as Double

For T In Temperatures      'T is inferred to be a Double
    'Do something
Next

我可以看到类型推断如何减少我编写一段代码所需的输入量,并且还可以更快地更改基本变量的类型(例如上面的Temperatures(10)),因为你不会需要更改访问它的所有其他变量的类型(例如T)。但是,我担心缺少明确的类型声明可能会使代码更难阅读,比如在代码检查期间,因为变量的类型可能不是很明显。例如,通过仅查看上面的 For 循环,您可能会正确地假设 T 是某个浮点值,但如果不回顾 @ 的声明,就无法判断它是单精度还是双精度987654325@。根据代码的编写方式,Temperatures 可以更早地声明,因此需要读者返回并找到声明,然后继续阅读。诚然,当使用一个好的 IDE 时,这并不是什么大问题,因为将鼠标光标快速悬停在变量名称上会显示类型。

另外,我想在某些情况下使用类型推断可能会在尝试更改代码时引入一些错误。考虑以下(诚然是人为的)示例,假设 AB 类做完全不同的事情。

Class A
    Public Sub DoSomething()
    End Sub
End Class

Class B
    Public Sub DoSomething()
    End Sub
End Class

Dim ObjectList(10) As A

For item In ObjectList
    item.DoSomething()
End For

可以将ObjectList 的类型从A 更改为B,代码可以编译,但功能不同,这可能表现为代码其他部分的错误。

通过一些类型推断的例子,我想大多数人都会同意它的使用对可读性或可维护性没有负面影响。例如,在 Java 7 中,可以使用以下代码:

ArrayList<String> a = new ArrayList<>();    // The constructor type is inferred from the declaration.

而不是

ArrayList<String> a = new ArrayList<String>();

我很想知道人们对这个问题的想法是什么,以及是否有任何关于应该使用多广泛类型推断的建议。

Amr

【问题讨论】:

  • 你的最后一个例子:ArrayList&lt;String&gt; a = new ArrayList&lt;&gt;(); 实际上是 Java 7 的新手。在此之前,程序员需要在右侧指定一个类型参数。
  • @TedHopp 感谢您指出这一点 - 我会澄清这个问题。
  • @Ted Hopp 感谢您的更正..
  • 恕我直言 this question will likely solicit opinion, debate, arguments, polling, or extended discussion. 适合程序员。

标签: java vb.net type-inference maintainability code-inspection


【解决方案1】:

就我个人而言,我倾向于仅在类型清晰且代码更易于阅读的情况下使用隐式类型推断

var dict = new Dictionary<string, List<TreeNode<int>>>();

更容易阅读
Dictionary<string, List<TreeNode<int>>> dict =
    new Dictionary<string, List<TreeNode<int>>>();

但类型是明确的。


这里不清楚你得到什么,我会明确指定类型:

var something = GetSomeThing();

在这里使用var 没有任何意义,因为它不会简化任何事情

int i = 0;
double x = 0.0;
string s = "hello";

【讨论】:

  • 我正在使用 DevExpess 的 Refactor! 工具,它具有“Make Explicit”重构功能。我通常先使用var,而不是输入长类型名称,然后在我更喜欢显式声明的情况下将其重构为显式。
【解决方案2】:

我认为部分原因在于编码风格。我个人选择了更详细的代码。 (虽然当我转向 Java 7 项目时,我可能会利用新语法,因为所有内容都在一行中。)

我也认为明确可以避免某些微妙的错误。举个例子:

For T In Temperatures      'T is inferred to be a Double
    'Do something
Next

如果 Do something 是一种依赖于 TDouble 的数值方法,那么将 Temperatures 更改为单精度可能会导致算法无法收敛或收敛到不正确的值。

我想我可以换一种说法——明确变量类型在某些情况下会产生很多本来可以避免的微不足道的维护工作。同样,我认为这完全是风格问题。

【讨论】:

    【解决方案3】:

    使用泛型是为了确保在编译时进行类型安全检查,而不是在运行时进行处理。如果您正确使用类型安全,它将使您免于运行时异常以及在数据检索时编写转换代码。

    在 jdk 1.7 之前,JAva 不使用此语法

    ArrayList<String> a = new ArrayList<>(); 
    

    它的

    ArrayList<String> a = new ArrayList(); 
    

    但是在java中使用

    ArrayList<String> a = new ArrayList(); and
    ArrayList<String> a = new ArrayList<String>();
    

    两者都认为相同,因为引用变量包含安全类型的编译时间信息,它是字符串,您不能在其中添加任何其他内容而不是字符串,即

    a.add(new Integer(5));
    

    它仍然是类型安全的,你会得到只能插入字符串的错误。

    所以你不能做你期望做的事情。

    【讨论】:

    • 第一个语法是 Java 7 的新语法。第二个是原始类型,它不是(完全)相同的东西。
    • Java 的 lint 程序会抱怨第二条语句,引用未经检查的转换。
    • @AmrBekhit “Java 的 lint 程序”到底是什么意思?编译器?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-26
    • 1970-01-01
    • 1970-01-01
    • 2021-04-15
    • 2021-09-08
    • 2015-12-28
    相关资源
    最近更新 更多