Java中的C#'var'关键字等效吗?


264

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


4
val(或var)如果您使用特定的“ Java替换”语言;-)

6
@pst:那会是Scala吗?嗯,是的。
rsenna 2010年

对于IntelliJ,我将其作为功能请求提交:youtrack.jetbrains.com/issue/IDEA-102808 IDE可以折叠代码以显示val或var,即使基础代码没有它。
乔恩·昂斯托特

@Jon我已经为IntelliJ一起破解了一些东西,请参阅我的答案
balpha

3
现在有人建议将此功能包含在Java中-openjdk.java.net/jeps/286
McGin

Answers:


248

空无一人。las,您必须输入完整的类型名称。

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

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

  • C#之所以使用var关键字是因为.NET中可能有没有名称的Types。例如:

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

    在这种情况下,不可能给赋予正确的类型myData。6年前,这在Java中是不可能的(所有类型都有名称,即使它们非常冗长和笨拙)。我不知道这是否已经改变。

  • vardynamicvariable仍然是100%静态键入的。这将无法编译:

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

    var currentUser = User.GetCurrent();

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

  • 这与无关var,但是,如果您有奇怪的继承层次结构,在这些层次结构中您将方法与其他方法(例如new public void DoAThing())一起使用,请不要忘记非虚拟方法会受到其转换为Type的影响。

    我无法想象现实世界中的情况表明良好的设计,但这可能无法按您预期的那样工作:

    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没有实际变量的非笨拙方法来初始化a 。

    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;

11
@Mike Caron:C#具有[默认]非虚拟调用,并且运算符不是虚拟的,因此... var p = new X(); p.Z()SuperX p = new X(); p.Z()所有X和SuperX 都不相同X : SuperX。用var静态型的p是总是X在上面第一个例子,但总是SuperX在第二个例子。需要注意的细微但重要的区别。但是您的答案非常正确:-)

200
@Jon Hanna:var不会使代码不清楚。我认为恰恰相反。例如,为什么在声明和实例化类型(RadioButton radioButton = new RadioButton();)时在同一行上写两次(甚至三遍)类型?var在命名变量时,您宁愿三思而后行,因为这将重点放在功能而不是类型上(例如 UserCollection collection = new userRepository.GetUsers();,更自然地变成var users = userRepository.GetUsers();)。如果您认为var不清楚,那只是因为没有使用它。
Martin Odhelius

4
@MartinOdhelius var绝对可以使代码清楚地被很好地使用,但是也可以使代码不清楚地被太坏地使用;像许多格式选项一样 余额的不同取决于您使用匿名对象和泛型的程度(.NET 1.0中都不存在),这使得它在C♯的第一个版本中作为假设关键字的用处较小。RadioButton radioButton在工厂或辅助方法的情况下,我只将按钮命名为a ,这是唯一的意义RadioButton,否则带有或不带有按钮的疯狂var
乔恩·汉娜

6
@乔恩·汉纳(Jon Hanna):我曾经对var你像批评一样,甚至不那么严格,但是我改变了我的观点,也许这就像一个不同观点的问题一样简单,因为当你说这是一个错误时,我仍然认为你是错的无论您使用了多少匿名对象和泛型;)类型声明通常只是代码噪音,如果您不理解它就无法理解代码,则在两种情况下代码都可能不清楚。
马丁·奥德休斯

3
您正在将var匿名类型混淆。var本身只是要求编译器从分配中推断类型;它是语法糖。一开始我对此表示怀疑,但是我虔诚地使用它,还没有遇到引起混乱的时期。它在实例化时消除了冗余,并在引用时消除了检查类型的需要。没有JAVA等同JAVA 9
Robear

46

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

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


1
@rightfold:其引用final不一定是不变的对象。考虑final StringBuilder strB = new StringBuilder("My reference is final"); strB.append("I'm not immutable");
Matthias Braun 2014年

1
从Lombok v1.16.12开始,也有针对的实验性支持varprojectlombok.org/features/experimental/var.html
Roel Spilker

@MatthiasBraun您的示例是错误的。在这种情况下,StringBuilder类本身是不可变的,您只需使用方法将字符串值添加到其内部结构中,这是完全合法的。
Andzej Maciusovic '17年


20

我已经为IntelliJ制作了一个插件,以某种方式为您提供var了Java语言。这是一种hack,因此通常的免责声明适用,但是如果您将IntelliJ用于Java开发并想尝试一下,请访问https://bitbucket.org/balpha/varsity


拥有键盘快捷键来在当前编辑器中折叠/展开声明类型将是很棒的。很棒的插件。
热键

2
@hotkey使用IntelliJ的内置代码折叠功能,因此您可以使用Ctrl-Shift-NumPadPlus展开所有内容。当光标在包含折叠变量声明的行上时,可以使用Ctrl-NumPadPlus和Ctrl-NumPadMinus折叠/展开当前方法中的声明。折叠所有声明有点尴尬,您已经折叠了所有内容(Ctrl-Shift-NumPadMinus),然后再次展开了所有内容(Ctrl-Shift-NumPadPlus)。
balpha

18

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

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

varJava中的保留类型名称与varC#中的关键字几乎相同,因为两者都允许隐式键入(有关重要​​区别,请参见下文)。varJava中的Java语言只能在以下情况下用于隐式类型推断(如JEP 286:Goals所枚举):

  • 带有初始值设定项的局部变量
  • 增强的for循环中的索引
  • 在传统的for循环中声明的本地人

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

我们试图通过减少与编写Java代码相关的仪式来改善开发人员的体验,同时通过允许开发人员取消经常不需要的局部变量类型的清单声明来维护Java对静态类型安全的承诺。

var Java范围界定

应当注意,这var不是Java中的关键字,而是保留的类型名称。如JEP 286所述:

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

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

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

如JEP 286所述:

这种处理将仅限于带有初始值设定项的局部变量,增强的for循环中的索引以及在传统的for循环中声明的局部变量;它不适用于方法形式,构造函数形式,方法返回类型,字段,catch形式或任何其他类型的变量声明。

varJava和C 之间的差异

这是varC#和Java 之间的显着区别,包括以下内容:var可以在C#中用作类型名称,但不能在Java中用作类名称或接口名称。根据C#文档(隐式键入局部变量)

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

var在C#中用作类型名称的能力会带来一些复杂性,并引入一些复杂的解析规则,而var在Java中,通过禁止var使用类或接口名称来避免了这些规则。有关varC#中类型名称的复杂性的信息,请参见限制适用于隐式类型的变量声明。有关Java中`var的范围界定决定背后的基本原理的更多信息,请参见JEP 286:范围选择


Java和c#之间的区别在于您不能var用于字段或返回类型?为什么要注意很重要?
user1306322

@ user1306322在这种情况下,不。varJava中的不能用于字段或返回类型。这很重要,因为此限制使var上下文敏感,该限制只能在某些上下文(局部变量)中使用,而不能在其他上下文中使用。这并不一定是Java和C#之间的区别,而是var在Java中使用时通常的重要限制。
贾斯汀·阿尔巴诺

我只是抬起头,似乎可以在c#中创建var一个类名并按原样使用它。从技术上讲,对于c#,它是一个“上下文关键字”,但是在Java中,看来您不能做同样的事情。如我错了请纠正我。
user1306322 '18

1
你是对的。您不能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。我已经更新了上面的答案,以更详细地了解varJava 背后的原理及其与varC#的区别。
贾斯汀·阿尔巴诺

11

JDK 10将支持它。甚至可以在早期访问版本中看到它的实际应用。

JEP 286

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

所以现在不用写:

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

你写:

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

笔记:

  • var现在是保留类型名称
  • Java 仍然致力于静态类型化!
  • 它只能在局部变量声明中使用

如果您想尝试一下而不在本地系统上安装Java,请创建一个安装了JDK 10 的Docker映像

$ 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 ==> []

3
请注意,您提供的代码(之前/之后var)并不等效。在var示例中list是类型ArrayList,而不是List
吉利

8

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

实际上,我只是添加了一个名为“ var”的类,因此不必键入其他内容。

该代码仍然太冗长,但是至少您不必键入它!


当您说“不错的IDE”时,是否排除了Eclipse *?-这似乎在Luna中不起作用(至少在我使用时才尝试过int)–我是否缺少某些东西?(*:虽然我永远都不会将Eclipse称为一个像样的IDE,但我无法判断其他人……)
BrainSlugs83 2014年

@ BrainSlugs83 dunno我正在使用IDEA,之前并未真正使用过eclipse。它不是适合您的类型吗?我习惯了c#/ visual studio / resharper,就像IDEA一样,除了它实际上可以正常工作之外!在所有的JetBrains公司的人,你可以按下Alt-Enter得到的建议列表时有一个错误-所以设置类型为INT介绍您可以按alt输入上的错误,并得到它排序的类型了
JonnyRaa

5

您可以通过JetBrains 看一下Kotlin,但这是瓦尔。不可变


4
Kotlin有val和var。val等价于在Java中声明变量final,而var允许重新分配。
samlewis

5

Java 10确实获得了局部变量类型推断,所以现在它具有var与C#相当的功能(据我所知)。

它也可以推断出非denotable类型(类型的不能用程序员的地点进行命名;虽然类型为非denotable是不同的)。参见例如带有var和匿名类的技巧(您永远不要在工作中使用)

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

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

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


@Lii是的,我不记得我的意思了,删除了。
Alexey Romanov

大!将删除我的评论以清理历史记录!
Lii

4

由于Java的10,相当于是... var


什么区别吗?我的意思是,您比写这样的简短答案要好得多,您的代表级别为n。当然肯定会有一些差异。
user1306322 '18

@ user1306322这是一个完全独立的问题!您可能会检查java-10标记,因为已经询问了该标记的许多方面。
Brian Goetz '18

好吧,这是您要检查问题,因为它是第一个出现在侧栏中的每个相关文章中的链接,它排在第一位。因此,我想,如果您知道回答该问题的单独问题,则可能至少可以链接到该问题或将其一部分包含在您的答案中。
user1306322

3

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


3

Lombok支持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);

是一个陷阱,以避免试图使用它的时候IntelliJ IDEA。尽管包括自动完成功能和所有功能,它似乎都能按预期工作。直到有一个“非hacky”解决方案为止(例如,由于JEP 286:本地变量类型推断)),这可能是您目前最好的选择。

请注意,valLombok也无需修改或创建即可提供支持lombok.config


2

您可以使用Java 10,但仅适用于本地变量,也就是说,

您可以,

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

但是不能

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

查看规范以获取更多信息。


2
这是不正确的。 var可用于多种形式的不可表示类型,包括捕获类型,交集类型和匿名类类型。并且,从Java 11开始,它也可以应用于lambda参数。
Brian Goetz '18

0

通常,您可以将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

@ user1306322我的答案有什么问题!你能详细说明吗?我们不能对所有数据类型使用Object类吗?
Aditya Kumar '18

对象对象= 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);
Aditya Kumar '18

问题是您不明白这个问题。问题不是“如何进行这项工作”,而是“是否有类似的事情”。您回答错误的问题。现在,var该词已正式成为该语言的用语,您的答案也指的是老式的方式。
user1306322

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.