【问题标题】:What is the equivalent of the C# 'var' keyword in Java?Java 中 C# 'var' 关键字的等价物是什么?
【发布时间】:2011-03-27 12:14:44
【问题描述】:

C# 中var 关键字的一种用法是隐式类型声明。 var 的 Java 等效语法是什么?

【问题讨论】:

  • val(或var)如果您使用特定的“Java 替代”语言;-)
  • @pst:那会是 Scala 吗?嗯,是的。
  • 对于 IntelliJ,我将此作为功能请求提交:youtrack.jetbrains.com/issue/IDEA-102808 即使底层代码没有,IDE 也可以折叠代码以显示 val 或 var。
  • @Jon 我已经为 IntelliJ 一起破解了一些东西,请参阅我的 answer
  • 现在有人提议将此功能包含在 Java 中 - openjdk.java.net/jeps/286

标签: java keyword var


【解决方案1】:

Java 10 确实获得了局部变量类型推断,所以现在它具有 var,这几乎等同于 C#(据我所知)。

它还可以推断不可表示的类型(程序员无法在该位置命名的类型;尽管 哪些 类型是不可表示的是不同的)。参见例如Tricks with var and anonymous classes (that you should never use at work).

我能找到的一个区别是在 C# 中,

If a type named var is in scope, then the var keyword will resolve to that type name and will not be treated as part of an implicitly typed local variable declaration.

在 Java 10 中,var 不是合法的类型名称。

【讨论】:

  • @Lii 是的,我不记得我在这里的意思,已删除。
  • 太棒了!将删除我的评论以清理历史记录!
【解决方案2】:

随着 3 月 20 日 JDK 10 的发布,Java 现在包括 JEP 286 中指定的 var 保留类型名称(不是关键字 — 见下文)。对于局部变量,以下内容现在在 Java 10 或更高版本中有效:

var map = new HashMap<String, Integer>();

Java 中的var 保留类型名称与C# 中的var 关键字几乎相同,因为两者都允许隐式类型(重要区别见下文)。 Java 中的var 只能用于以下上下文中的隐式类型推断(如JEP 286: Goals 中所列举):

  • 带有初始化器的局部变量
  • 增强的 for 循环中的索引
  • 在传统 for 循环中声明的本地变量

因此var不能用于字段、返回类型、类名或接口名。其基本原理是在声明和定义局部变量时不再需要包含长类型名称,如 JEP 286(由 Brian Goetz 撰写)中所述:

我们力求通过减少仪式来改善开发者体验 与编写 Java 代码相关,同时维护 Java 的承诺 为了静态类型安全,允许开发人员省略 经常不必要的局部变量类型的清单声明。

var Java 中的作用域

需要注意的是var在Java中不是关键字,而是保留的类型名。引用自 JEP 286:

标识符 var 不是关键字;相反,它是保留类型 姓名。这意味着使用 var 作为变量、方法或 包名不受影响;使用 var 作为类的代码或 接口名称会受到影响(但这些名称在实践中很少见, 因为它们违反了通常的命名约定)。

请注意,由于var 是保留类型名称而不是关键字,它仍然可以用于包名称、方法名称和变量名称(以及它的新类型干扰角色)。例如,以下是 Java 中var 的有效使用示例:

var i = 0;
var var = 1;
for (var i = 0; i < 10; i++) { /* ... */ }
public int var() { return 0; }
package var;

引用自 JEP 286:

这种处理将仅限于局部变量 增强型 for 循环中的初始化程序、索引和在 传统的 for 循环;它不适用于方法形式, 构造函数形式、方法返回类型、字段、捕获形式,或 任何其他类型的变量声明。

var 在 Java 和 C 中的区别

这是 C# 中的 var 和 Java 之间的一个显着区别,包括以下内容:var 在 C# 中可以用作类型名称,但在 Java 中不能用作类名称或接口名称。根据C# documentation (Implicitly Typed Local Variables)

如果名为var 的类型在范围内,则var 关键字将解析为 该类型名称并且不会被视为隐式类型的一部分 局部变量声明。

在 C# 中使用 var 作为类型名称的能力会产生一些复杂性并引入一些复杂的解析规则,Java 中的 var 通过禁止将 var 作为类或接口名称来避免这些问题。有关 C# 中 var 类型名称的复杂性的信息,请参阅 Restrictions apply to implicitly-typed variable declarations。有关 Java 中 `var 的作用域决定背后的基本原理的更多信息,请参阅JEP 286: Scoping Choices

【讨论】:

  • Java 和 c# 有什么区别,不能使用 var 来表示字段或返回类型?为什么要注意?
  • @user1306322 在这种情况下,不。 Java 中的var 不能用于字段或返回类型。这很重要,因为此限制使var 上下文敏感,它只能在某些上下文(局部变量)中使用,而不能在其他上下文中使用。这不一定是 Java 和 C# 之间应该注意的区别,而是在 Java 中使用 var 时通常的一个重要限制。
  • 我刚刚查了一下,似乎在 c# 中您可以将 var 设为类名并照此使用。从技术上讲,在 c# 中它是一个“上下文关键字”,但在 Java 中,您似乎不能这样做。如果我错了,请纠正我。
  • 你是对的。您不能将var 用作Java 中的类名或接口名(这并不常见),但您可以将其用于变量名、方法名和包名。例如,var var = 1; 是有效的 Java 语句,但尝试将类声明为 public class var {} 会导致错误:as of release 10, 'var' is a restricted local variable type and cannot be used for type declarations。我已经更新了上面的答案,以更详细地了解 Java 中 var 背后的基本原理及其与 C# 中 var 的区别。
【解决方案3】:

您可以,在 Java 10 中,但仅限于 Local 变量,意思是,

你可以,

var anum = 10; var aString = "Var";

但不能,

var anull = null; // Since the type can't be inferred in this case

查看spec 了解更多信息。

【讨论】:

  • 这是不正确的。 var 可用于多种形式的不可表示类型,包括捕获类型、交集类型和匿名类类型。而且,从 Java 11 开始,它也可以应用于 lambda 参数。
【解决方案4】:

没有。唉,你必须输入完整的类型名称。

编辑:发布 7 年后,Java 10 中添加了局部变量的类型推断(使用 var)。

编辑:发布6年后,从下面收集一些cmets:

  • C# 有var 关键字的原因是因为在.NET 中可能有没有名称的类型。例如:

    var myData = new { a = 1, b = "2" };
    

    在这种情况下,不可能为myData 提供正确的类型。 6 年前,这在 Java 中是不可能的(所有类型都有名称,即使它们非常冗长和笨拙)。我不知道这是否同时发生了变化。

  • vardynamic 不同。 variables 仍然是 100% 静态类型的。这不会编译:

    var myString = "foo";
    myString = 3;
    
  • var 在类型从上下文中显而易见时也很有用。例如:

    var currentUser = User.GetCurrent();
    

    我可以说,在我负责的任何代码中,currentUser 中都有一个User 或派生类。显然,如果您的 User.GetCurrent 实现返回一个 int,那么这可能对您不利。

  • 这与var 无关,但是如果您有奇怪的继承层次结构,其中您使用其他方法(例如new public void DoAThing())隐藏方法,请不要忘记非虚拟方法受它们所在的类型的影响投射为。

    我无法想象在现实世界中这表明了良好的设计,但这可能不会像您期望的那样工作:

    class Foo {
        public void Non() {}
        public virtual void Virt() {}
    }
    
    class Bar : Foo {
        public new void Non() {}
        public override void Virt() {}
    }
    
    class Baz {
        public static Foo GetFoo() {
            return new Bar();
        }
    }
    
    var foo = Baz.GetFoo();
    foo.Non();  // <- Foo.Non, not Bar.Non
    foo.Virt(); // <- Bar.Virt
    
    var bar = (Bar)foo;
    bar.Non();  // <- Bar.Non, not Foo.Non
    bar.Virt(); // <- Still Bar.Virt
    

    如前所述,虚方法不受此影响。

  • 不,没有任何简单的方法可以在没有实际变量的情况下初始化 var

    var foo1 = "bar";        //good
    var foo2;                //bad, what type?
    var foo3 = null;         //bad, null doesn't have a type
    var foo4 = default(var); //what?
    var foo5 = (object)null; //legal, but go home, you're drunk
    

    在这种情况下,只需按照老式的方式进行:

    object foo6;
    

【讨论】:

  • @Mike Caron: C# 有 [default] 非虚拟调用和操作符不是虚拟的,所以...var p = new X(); p.Z() 与所有 X 和 SuperX 的 SuperX p = new X(); p.Z() 不同,即使X : SuperX。对于varpstatic 类型在上面的第一个示例中始终为 X,但在第二个示例中始终为 SuperX。需要注意的一个微妙但重要的区别。但你的答案非常正确:-)
  • @Jon Hanna:var 不会让代码变得不那么清晰。在我看来恰恰相反。例如,为什么在声明和实例化类型时在同一行上写两次(甚至三次)次(RadioButton radioButton = new RadioButton();)? var 让您在命名变量时要三思而后行,因为它会将焦点转向功能而不是类型(例如,UserCollection collection = new userRepository.GetUsers(); 更自然地变成了var users = userRepository.GetUsers();)。如果您认为var 不清楚,那只是因为不习惯。
  • @MartinOdhelius var 很好用肯定可以让代码清晰,但是用不好也可以让代码清晰;像许多格式化选项一样。平衡取决于您使用匿名对象和泛型的程度,这两者在 .NET 1.0 中都不存在,这使得它在 C♯ 的第一个版本中作为假设关键字的用处不大。我只会在工厂或辅助方法的情况下命名RadioButtonradioButton,其中按钮唯一重要的是它是RadioButton,否则无论有没有var,这都是疯狂的。
  • @Jon Hanna: 我曾经对var 和你一样挑剔,如果不是更多的话,但我已经改变了我的看法,也许这就像一个不同意见的问题一样简单,因为我当你说你使用多少匿名对象和泛型时仍然认为你是错的;)类型声明通常只是代码噪音,如果没有它你无法理解代码,那么代码在任何一种情况下都可能不清楚.
  • 您将varanonymous types 混淆了。 var 本身只是要求编译器从赋值中推断类型;它是语法糖。起初我对它持怀疑态度,但我虔诚地使用它,还没有遇到过它引起混乱的时候。它在实例化时消除了冗余,并消除了在引用时检查类型的需要。从 JAVA 9 开始没有 JAVA 等价物。
【解决方案5】:

这个特性现在在 Java SE 10 中可用。静态的、类型安全的 var 终于进入了 java 世界:)

来源:https://www.oracle.com/corporate/pressrelease/Java-10-032018.html

【讨论】:

    【解决方案6】:

    从 Java 10 开始,等效为 ...var

    【讨论】:

    • 任何差异吗?我的意思是你知道最好不要写这样的简短答案,你的代表水平如何。并且肯定会有一些差异。
    • @user1306322 这是一个完全独立的问题!您可能会检查 java-10 标记,因为这方面的许多方面已经被询问过。
    • 好吧,如果您在谷歌上搜索,这是 the 问题,因为它排在第一位,并且在侧边栏中的每个相关帖子中都有链接。所以我想如果你知道那个单独的问题可以回答它,你至少可以链接到它或将它的一部分包含在你的答案中。
    【解决方案7】:

    如果您将 Lombok 添加到您的项目中,您可以使用它的 val 关键字。

    http://projectlombok.org/features/val.html

    【讨论】:

    • @rightfold:引用为final 的对象不一定是不可变的。考虑final StringBuilder strB = new StringBuilder("My reference is final"); strB.append("I'm not immutable");
    • 自 lombok v1.16.12 以来,还提供了对 var 的实验性支持。 projectlombok.org/features/experimental/var.html
    • @MatthiasBraun 你的例子是错误的。在这种情况下 StringBuilder 类本身是不可变的,你只需使用方法将字符串值添加到其内部结构中,这是完全合法的。
    【解决方案8】:

    一般来说,您可以将 Object 类用于任何类型,但稍后您必须进行类型转换!

    例如:-

    Object object = 12;
        Object object1 = "Aditya";
        Object object2 = 12.12;
    
        System.out.println(Integer.parseInt(object.toString()) + 2);
    
        System.out.println(object1.toString() + " Kumar");
        System.out.println(Double.parseDouble(object2.toString()) + 2.12);
    

    【讨论】:

    • Aditya,请检查您的所有其他答案。他们中的许多人都有-1,这可能是有充分理由的。如果您不确定它是否正确,请不要随便发布。
    • @user1306322 我的回答有什么问题!你能详细说明一下吗?我们不能对所有数据类型使用 Object 类吗?
    • 对象对象 = 12;对象 object1 = "Aditya";对象 object2 = 12.12; System.out.println(Integer.parseInt(object.toString()) + 2); System.out.println(object1.toString() + "Kumar"); System.out.println(Double.parseDouble(object2.toString()) + 2.12);
    • 问题是你没有理解问题。问题不是“如何使这项工作”,而是“是否有这样的事情”。你回答错了问题。随着var 这个词现在正式在语言中,你的回答也指的是现在老式的方式。
    【解决方案9】:

    它将在 JDK 10 中得到支持。甚至可以在 early access build 中看到它的实际应用。

    JEP 286:

    增强 Java 语言以将类型推断扩展到使用初始化程序的局部变量声明。

    所以现在不要写了:

    List<> list = new ArrayList<String>();
    Stream<> stream = myStream();
    

    你写:

    var list = new ArrayList<String>();
    var stream = myStream();
    

    注意事项:

    • var 现在是 reserved type name
    • Java仍然致力于静态类型!
    • 只能在局部变量声明中使用

    如果您想在本地系统上不安装 Java 的情况下试一试,我创建了一个安装了 JDK 10 的 Docker image

    $ docker run -it marounbassam/ubuntu-java10 bash
    root@299d86f1c39a:/# jdk-10/bin/jshell
    Mar 30, 2018 9:07:07 PM java.util.prefs.FileSystemPreferences$1 run
    INFO: Created user preferences directory.
    |  Welcome to JShell -- Version 10
    |  For an introduction type: /help intro
    
    jshell> var list = new ArrayList<String>();
    list ==> []
    

    【讨论】:

    • 请注意,您提供的代码(var 之前/之后)不等效。在 var 示例中,list 的类型为 ArrayList,而不是 List
    【解决方案10】:

    一个简单的解决方案(假设您使用的是不错的 IDE)是在任何地方都输入“int”,然后让它为您设置类型。

    我实际上只是添加了一个名为“var”的类,所以我不必输入不同的内容。

    代码还是太冗长了,不过至少不用打字!

    【讨论】:

    • 当您说“体面的 IDE”时是否排除了 Eclipse*? -- 这似乎在 Luna 中不起作用(至少当我刚刚用int 尝试它时) -- 我错过了什么吗? (*:虽然我永远不会称 Eclipse 为像样的 IDE,但我无法判断其他人......)
    • @BrainSlugs83 不知道我正在使用 IDEA,之前没有真正使用过 eclipse。它不适合您吗?我习惯了 c#/visual studio/resharper,它就像 IDEA,但它实际上工作正常!在所有的 jetbrains 中,当出现错误时,您可以按 alt-enter 来获取建议列表 - 因此将 type 设置为 int 会引入一个错误,您可以 alt-enter 并让它对类型进行排序
    【解决方案11】:

    Lombok supports var 但它仍被归类为实验性:

    import lombok.experimental.var;
    
    var number = 1; // Inferred type: int
    number = 2; // Legal reassign since var is not final
    number = "Hi"; // Compilation error since a string cannot be assigned to an int variable
    System.out.println(number);
    

    Here 是在IntelliJ IDEA 中尝试使用它时要避免的一个陷阱。它似乎按预期工作,但包括自动完成和一切。在出现“非 hacky”解决方案之前(例如,由于 JEP 286: Local-Variable Type Inference),这可能是您现在最好的选择。

    请注意val 也受Lombok 的支持,无需修改或创建lombok.config

    【讨论】:

      【解决方案12】:

      JEP - JDK 增强提案

      http://openjdk.java.net/jeps/286

      JEP 286:局部变量类型推断

      作者布莱恩·戈茨

      // Goals:
      var list = new ArrayList<String>();  // infers ArrayList<String>
      var stream = list.stream();          // infers Stream<String>
      

      【讨论】:

      • 什么版本的Java? 10 点?
      • @PeterMortensen 是的。 JEP 286 已被接受并在 JDK 10 中发布(请参阅 openjdk.java.net/projects/jdk/10 的功能下的 286)。
      【解决方案13】:

      您可以查看 JetBrains 的 Kotlin,但它是 val。不是变量。

      【讨论】:

      • Kotlin 有 val 和 var。 val 相当于在 java 中声明一个变量 final,var 允许重新赋值。
      【解决方案14】:

      我知道这是较旧的,但为什么不创建一个 var 类并创建具有不同类型的构造函数,并且根据调用的构造函数,您会得到具有不同类型的 var。您甚至可以构建将一种类型转换为另一种类型的方法。

      【讨论】:

        【解决方案15】:

        我为 IntelliJ 制作了一个插件,在某种程度上,它在 Java 中为您提供var。这是一个 hack,所以通常的免责声明适用,但如果您使用 IntelliJ 进行 Java 开发并想尝试一下,它位于 https://bitbucket.org/balpha/varsity

        【讨论】:

        • 在当前编辑器中有一个键盘快捷键来折叠/展开声明类型会很棒。很棒的插件。
        • @hotkey 这使用 IntelliJ 的内置代码折叠功能,因此您可以使用 Ctrl-Shift-NumPadPlus 展开所有内容。当光标位于包含折叠变量声明的行上时,您可以 Ctrl-NumPadPlus 和 Ctrl-NumPadMinus 折叠/展开当前方法中的声明。折叠所有声明有点尴尬,你折叠了所有东西(Ctrl-Shift-NumPadMinus)然后再次展开所有东西(Ctrl-Shift-NumPadPlus)。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-10-30
        • 1970-01-01
        • 2019-04-04
        • 2010-12-07
        • 2010-11-22
        • 1970-01-01
        相关资源
        最近更新 更多