简体   繁体   English

Java对象创建语法的效率?

[英]Java object creation syntax efficiency?

This may sound really basic. 这听起来可能很基础。 But I'm brand new to Java. 但是我是Java的新手。 With the few initial hours of learning I've put in so far, I'm continuously perplexed by the redundancy in the syntax of a new object declaration: 到目前为止,我已经投入了最初的几个小时的学习时间,但新对象声明语法的冗余使我不断困惑:

TypeName a = new TypeName();

In particular, 特别是,

String s = new String("abc");
Character c = new Character("A");

Why in the world would someone want to type the keyword TypeName (eg. String , Character , etc...) twice ? 为什么世界上有人要两次键入关键字TypeName (例如StringCharacter等)? I understand there are short-hands of: 我了解以下方面的捷径:

String s = "abc";
char c = "A";

But these are exceptions rather than rules. 但是这些是例外而不是规则。 So can any one enlighten me please? 那么任何人都可以启发我吗? Thx. 谢谢。

Because sometimes you want to do something like: 因为有时候您想做类似的事情:

// Explicitly force my object to be null
String s = null;

or 要么

// Cast a child class to its parent
MyParentClass mpc = new IneritedClassFromParent();

or 要么

// Store and access a concrete implementation using its interface
ISomeInterface isi = new ConcreteInterfaceImplementation();

In other words, just because you declare a type to store doesn't always mean you want it initialized with a new instance of that class. 换句话说,仅仅因为您声明要存储的类型并不总是意味着您希望使用该类的新实例对其进行初始化。

You may want to use a new instance of a child class when using inheritance or an Interface Implementation when using Interfaces. 使用继承时可能要使用子类的新实例,使用接口时可能要使用接口实现。

Or sometimes you may want to explicitly force something to be null initially and fill it later. 或者有时您可能想明确地强制某些内容最初为null,然后再填充。

使用此语法,您可以轻松创建X类型的对象并将其分配给Y类型的变量:

List<String> myList = new ArrayList<String>();

Why in the world would someone want to type the keyword TypeName (eg. String, Character, etc...) twice? 为什么在世界上有人想要两次键入关键字TypeName(例如,字符串,字符等)?

Because you're doing two things: 因为您正在做两件事:

  • Declaring a variable of a certain type 声明某种类型的变量
  • Creating an object of a certain type 创建某种类型的对象

The two types are not necessarily the same, eg 两种类型不一定相同,例如

Map m = new HashMap();

You are probably used to "dynamically typed" languages like PHP where variables do not have a type. 您可能已经习惯了像PHP这样的“动态类型化”语言,其中变量没有类型。 The advantage you get with Java's static type declarations is that a lot of programming errors are caught by the compiler (even, in a modern IDE, while you are typing). 使用Java的静态类型声明所获得的好处是,编译器会捕获很多编程错误(即使在键入时在现代IDE中也是如此)。 For example, if you make a simple mistake: 例如,如果您犯了一个简单的错误:

m.siez();

The compiler will immediately alert you to the fact that there is something wrong with your program - and it can do that only because it knows that the declared type Map does not have a method siez() . 编译器会立即提醒您程序存在问题-它之所以能够这样做,仅是因为它知道声明的类型Map没有方法siez()

Some modern statically typed languages like C# and Scala use type inference to give you a "best of both worlds" where you can omit the type declaration and the compiler will assume that it's the same as the type of the object you are assigning to it. 一些现代的静态类型化语言(例如C#和Scala)使用类型推断为您提供“两全其美”,您可以在其中省略类型声明,并且编译器会假定该声明与您为其分配的对象的类型相同。 However, such languages always allow explicit type declarations because type inference is not always possible or desirables (such as in the example above where the variable is supposed to use the interface rather than the concrete class). 但是,此类语言始终允许显式类型声明,因为类型推断并非总是可能或不合需要的(例如,在上面的示例中,该变量应该使用接口而不是具体的类)。

This is not at all redundant. 这根本不是多余的。 There are two steps to utilize a variable : 利用变量有两个步骤:

  1. declaration : this step tells the VM what will be the static footprint of the variable. 声明 :此步骤告诉VM变量的静态足迹是多少。 For instance : Object a; 例如: Object a; will have only the footprint declared in the class Object visible, while Integer b; 仅在类Object声明的覆盖区可见,而Integer b; will have all footprint declared in the Integer class and all inherited parent classes, up to Object visible. 将在Integer类和所有继承的父类中声明所有足迹,直到Object可见。 This is for the static part. 这是针对静态部分的。

  2. instanciation : this step tells the VM what will be the dynamic footprint of the variable. 实例化 :此步骤告诉VM变量的动态足迹是多少。 For instance : List<String> c = new LinkedList<String>(); 例如: List<String> c = new LinkedList<String>(); , then c.put("foo"); ,然后c.put("foo"); will use the LinkedList 's implementation of the put() method, even if what is visible is List::put() . 即使List::put()可见,也将使用LinkedListput()方法的实现。 Sometimes, you will require that kind of declaration/instanciation, but will need to override to access a very specific method not visible with the static footprint. 有时,您将需要这种声明/实例化,但是将需要重写以访问静态足迹不可见的非常特定的方法。 For example, let's consider a method declared as public void method1(Object obj) and you know that the obj instance is actually an Integer , thus you will specifically use the dynamic footprint by casting the object into it: int value = ((Integer) obj).intValue(); 例如,让我们考虑一个声明为public void method1(Object obj) ,您知道 obj实例实际上是一个Integer ,因此您可以通过将对象强制转换为它来专门使用动态足迹: int value = ((Integer) obj).intValue();

Now, as for the String a = "A"; 现在,对于String a = "A"; part. 部分。 Java has made shorthand writing of the "primitive" classes available for simplicity. 为了简化起见,Java提供了“原始”类的速记形式。 More specifically, since Java 1.5, you can do : 更具体地说,从Java 1.5开始,您可以执行以下操作:

Integer n1 = 1;
Integer n2 = new Integer(1);  // same thing
int n3 = n2;

And all works. 和所有的作品。 But what's the difference? 但是有什么区别呢? Consider this piece of code : 考虑这段代码:

String a = new String("A");
String b = new String("A");
String c = "A";
String d = "A";

System.out.println("a:" + a.hashCode() + " = b:" + b.hashCode() + " == " + (a == b));
System.out.println("b:" + b.hashCode() + " = c:" + c.hashCode() + " == " + (b == c));
System.out.println("c:" + c.hashCode() + " = d:" + d.hashCode() + " == " + (c == d));

will output 将输出

a:65 = b:65 == false
b:65 = c:65 == false
c:65 = d:65 == true

Why? 为什么? Because the JVM is trying to reuse memory as much as possible, and since a and b are creating new instances of a String , they are not sharing the same memory space. 因为JVM试图尽可能多地重用内存,并且由于ab正在创建String新实例,所以它们没有共享相同的内存空间。 However, c and d are using constant string values (this is a compiler optimization) and therefore are pointing to the exact same String object. 但是, cd使用常量字符串值(这是编译器优化),因此指向完全相同的String对象。

Many good answers here as to why it's necessary. 为何需要这样做,这里有许多很好的答案。 You're correct in that it often seems redundant. 您是对的,因为它通常看起来很多余。 java has often been (not unfairly) criticized as a bit, er... verbose. Java经常(不公平地)受到批评,有点...冗长。 There are a few shortcuts. 有一些快捷方式。 eg for strings String s="Abc" (not actually a shortcut, it's a bit different, and better, in that you're not explicitly creating a new object). 例如,对于字符串String s="Abc" (实际上不是快捷方式,它有所不同,并且更好,因为您没有显式创建新对象)。 There will also be some reduction of the duplication in declarations in java 7 for generics. Java 7中针对泛型的声明中的重复项也将有所减少。

That's because there is no implicit way to assign the value of a complex object. 这是因为没有隐式方法可以分配复杂对象的值。

When you do int a = 3; 当你做int a = 3; or double b = 2.5; double b = 2.5; , you can implicitly state the type on the right-hand side. ,您可以在右侧隐式声明类型。

In OOP, you have to use a constructor which is why you have to do new TypeName() . 在OOP中,必须使用构造函数,这就是为什么必须执行new TypeName() This also gives you the ability to pass in parameters to set up the object. 这也使您能够传递参数来设置对象。

Another reason is when you use interfaces. 另一个原因是使用接口时。 So you can do: 因此,您可以执行以下操作:

MyInterface blah = new InterfaceImplementation();
MyInterface bar = new AnotherInterfaceImplementation();

and even: 乃至:

ParentClass foo = new DerivedClass();

This is because when working with interfaces you typically don't want to set the variable type to be the interface-implementation, but the interface itself. 这是因为在使用接口时,您通常不想将变量类型设置为接口实现,而是将接口本身设置为。 Otherwise there would be no way to specify which implementation to use. 否则,将无法指定要使用的实现。

Another useful thing is generics: 另一个有用的东西是泛型:

List<SomeType> myList = new ArrayList<SomeType>();

Java 7 will simplify this to Java 7会将其简化为

List<SomeType> myList = new ArrayList<>();

so that you don't have to type <SomeType> twice (this especially gets painful in Maps). 这样您就不必键入<SomeType>两次(这在Maps中尤其<SomeType> )。

When you will get to the class generics (extensions): 当您到达类的泛型(扩展名)时:

class a extends b

You will see that you can do something like this: 您将看到可以执行以下操作:

b=new a();

Well, a variable needs to have a type. 好吧,变量需要具有类型。 And when you are creating instance of an object, you need to tell which type it should be. 在创建对象的实例时,您需要确定对象的类型。 And of course these don't have to be the same. 当然,这些不必相同。 You can set a String to an Object variable, for instance. 例如,您可以将String设置为Object变量。 Of course you could have something like this to make things a bit easier: 当然,您可以使用类似的方法使事情变得容易一些:

var s = new TypeName();

That's how it has been done in C#. 这就是在C#中完成的方式。 But I suppose in Java they have not seen a need for that. 但是我想在Java中他们没有看到这样做的必要。
I agree that Java is quite verbose by modern standards, but it also quite easy to read and there's not much syntactic sugar to confuse you. 我同意Java在现代标准上相当冗长,但是它也很容易阅读,并且没有太多语法上的糖令您感到困惑。

There are strongly typed languages that support "type inference" (like Scala, for example). 有一些强类型语言支持“类型推断”(例如Scala)。 Java simply isn't one of them (although there will be some type inference of generic arguments in Java 7). Java根本不是其中之一(尽管Java 7中会有一些泛型参数的类型推断)。 In these languages, although the variable type isn't declared, the compiler can unambiguously infer it, and still detect type errors. 在这些语言中,尽管未声明变量类型,但编译器可以明确地推断出它,并仍然检测类型错误。 For example (non-Java): 例如(非Java):

val str = "This is not a number!";
val x = str.intValue(); // Compiler error, because str is implicitly a String.

In Java, there will be many cases where you will assign a specific concrete type on the right to a more general type on the left. 在Java中,在许多情况下,您会将右侧的特定具体类型分配给左侧的更常规的类型。 For example: 例如:

Set students = new TreeSet();

This is a good style, because your code will not depend on a specific implementation. 这是一种很好的样式,因为您的代码将不依赖于特定的实现。 If you need to switch implementations (for example, you need faster lookups from a hash-based Set ), only the right-hand side of the initializer changes. 如果需要切换实现(例如,需要从基于哈希的Set更快的查找),则仅初始化程序的右侧会更改。

For this reason, it's especially important to declare public APIs using the correct abstractions, rather than concrete types. 因此,使用正确的抽象而不是具体类型声明公共API尤为重要。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM