为什么按原样创建实例?


17

在过去六个月的时间里,我已经学习了C#,现在正在研究Java。我的问题是关于实例创建(实际上是用两种语言)的问题,而更多的是:我想知道为什么他们这样做了。举个例子

Person Bob = new Person();

是否有理由两次指定对象?会不会有something_else Bob = new Person()

如果我遵循惯例,似乎会更像:

int XIsAnInt;
Person BobIsAPerson;

也许是其中之一:

Person() Bob;
new Person Bob;
new Person() Bob;
Bob = new Person();

我想我很好奇是否有比“这就是完成的方式”更好的答案。


26
如果Person是的子类型LivingThing怎么办?你可以写LivingThing lt = new Person()。寻找继承和接口。
xlecoustillier 2015年

2
Person Bob声明类型为“引用” 的变量Person称为Bobnew Person()创建一个Person对象。引用,变量和对象是三件事!
user253751'2

5
您是否对冗余感到恼火?那为什么不写var bob = new Person();呢?
200_success 2015年

4
Person Bob();在C ++中是可能的,并且含义几乎与Person Bob = Person();
user60561

3
@ user60561不,它声明一个不带参数并返回Person的函数。
Nikolai 2015年

Answers:


52

会不会有something_else Bob = new Person()?

是的,因为继承。如果:

public class StackExchangeMember : Person {}

然后:

Person bob = new StackExchangeMember();
Person sam = new Person();

鲍勃也是一个人,而且很高兴,他不想和其他人一样受到不同对待。

此外,我们可以赋予鲍勃超能力:

public interface IModerator { }
public class StackOverFlowModerator : StackExchangeMember, IModerator {}

IModerator bob = new StackOverFlowModerator();

因此,根据天哪,他绝不会像其他主持人一样受到不同对待。而且他喜欢在论坛上潜行,以使所有人在隐身状态下保持一致:

StackExchangeMember bob = new StackOverFlowModerator();

然后,当他找到一些可怜的第一张海报时,他甩开了自己的隐形斗篷,扑了扑过去。

((StackOverFlowModerator) bob).Smite(sam);

然后,他可以随后采取所有无辜的行动:

((Person) bob).ImNotMeanIWasJustInstantiatedThatWay();

20
如果您将对象名称小写,这将更加清楚。
与莫妮卡(Monica)进行的轻度比赛

38
规范的好例子;继承的不好例子。对于其他阅读本文的人,请不要尝试使用继承解决用户角色。
Aaronaught

8
@Aaronaught是正确的。不要为不同类型的人创建单独的类。使用位域enum
科尔·约翰逊

1
@Aaronaught说不该做的事很好,但是如果不说人们应该做什么,那不是很有帮助。
法拉普2015年

5
@Pharap:我确实在其他几个 问题上做到了。简单的答案是,应将用户(身份验证/身份)和安全策略(授权/许可)视为独立的问题,并且安全策略的标准模型基于角色或基于声明。继承对于描述实际进行身份验证的对象(例如LDAP实现和SQL实现)更有用。
Aaronaught 2015年

34

让我们来看一下第一行代码。

Person Bob = new Person();

第一个Person类型规范。 在C#中,我们可以通过简单地说

var Bob = new Person();

然后编译器从构造器调用中推断变量Bob的类型Person()

但是您可能想要编写如下内容:

IPerson Bob = new Person();

如果您不履行Person的全部API合同,而仅履行interface指定的合同IPerson


2
+1:我将IPerson在代码中使用示例,以确保在编写应可复制/粘贴到另一个IPerson实现的代码时,不会意外使用任何私有方法。
Cort Ammon-恢复莫妮卡2015年

@CortAmmon我认为您那里有一个错字,很明显,您的意思是“使用多态”,而不是在该代码上进行复制/粘贴:D
Benjamin Gruenbaum

@BenjaminGruenbaum当然,对于多态性的某些定义;-)
Cort

@CortAmmon您将如何意外地调用私有方法?当然是您的意思internal吗?
科尔·约翰逊

@ColeJohnson两种方式。我写的是考虑到我在哪里遇到的一个特定案例的private意义:工厂方法是实例化类的一部分。在我的情况下,获得私有价值观应该是例外,而不是常规。我从事的代码将使我长寿。如果以这种方式进行操作,不仅我自己不太可能使用任何私有方法,而且当下一个开发人员将其复制/粘贴到几十个位置并在其后复制它们时,这会降低有人看到机会进行复制的可能性。使用私有方法作为“正常”行为。
Cort Ammon-恢复莫妮卡2015年

21
  1. 这种语法几乎是C ++的传统,顺便说一下,它同时具有以下两种功能:

    Person Bob;

    Person *bob = new Bob();

    第一个创建当前范围内的对象,第二个创建指向动态对象的指针。

  2. 你绝对可以有 something_else Bob = new Person()

    IEnumerable<int> nums = new List<int>(){1,2,3,4}

    您在这里做了两件事,说明了局部变量的类型,nums并且说您想创建一个类型为“ List”的新对象并将其放在那里。

  3. C#同意您的观点,因为在大多数情况下,变量的类型与您输入的变量相同,因此:

    var nums = new List<int>();
  4. 在某些语言中,您会尽力避免像F#中那样声明变量的类型:

    let list123 = [ 1; 2; 3 ]

5
说第二个示例创建指向新Bob对象的指针可能更准确。从技术上讲,事物的存储方式是一种实现细节。
罗伯特·哈维

4
“第一个在堆栈上创建本地对象,第二个在堆上创建对象。” 哦,老兄,再也不会出现这种错误信息了。
与莫妮卡(Monica)进行的轻度比赛

@LightnessRacesinOrbit更好?
AK_

1
@AK_虽然“动态”在很大程度上意味着“堆”(当堆是平台使用的内存模型时),但“自动”与“堆”截然不同。如果您这样做了new std::pair<int, char>(),那么这对成员中的成员firstsecond具有自动的存储期限,但是它们很可能是在堆上分配的(作为dynamic-storage-duration pair对象的成员)。
恢复莫妮卡

1
@AK_:是的,这些名称恰恰暗示了它们的含义,而这种“堆”与“堆”的废话却不是。这就是重点。您发现它们令人困惑,只会增加我们教他们的必要性,因此您不会仅仅因为熟悉而依赖不准确/不正确的术语!
与莫妮卡(Monica)进行的轻量级比赛,2015年

3

int x和之间存在巨大差异Person bob。an int是an intint并且它必须始终是an int,并且永远不能是an int。即使您未int在声明时初始化int x;,也仍将其int设置为默认值。

Person bob但是,在声明时,名称bob在任何给定时间实际指代的内容都具有很大的灵活性。它可以指代a Person,也可以指代其他一些类,例如ProgrammerPerson; 派生的。它甚至可能是null,根本没有引用任何对象。

例如:

  Person bob   = null;
  Person carol = new Person();
  Person ted   = new Programmer();
  Person alice = personFactory.functionThatReturnsSomeKindOfPersonOrNull();

语言设计人员当然可以做出替代语法,Person carol = new Person()用更少的符号实现相同的目的,但是他们仍然必须允许Person carol = new Person() (或制定一些奇怪的规则,使上述四个示例中的特定示例不合法)。他们更关心保持语言“简单”,而不是编写极其简洁的代码。这可能影响了他们决定不提供更短的替代语法的决定,但是在任何情况下,都是没有必要的,他们也没有提供。


1

这两个声明可以不同,但​​通常是相同的。Java中常见的推荐模式如下:

List<String> list = new ArrayList<>();
Map<String, Integer> map = new HashMap<>();

这些变量listmap是使用接口声明的ListMap同时代码实例化了特定的实现。这样,其余代码仅取决于接口,并且很容易选择不同的实现类进行实例化,例如TreeMap,因为其余代码不能依赖于接口HashMap外部API的任何部分Map

两种类型不同的另一个示例是在工厂方法中,该方法选择要实例化的特定子类,然后将其作为基本类型返回,因此调用方不必知道实现细节,例如“策略”选择。

类型推断可以解决源代码冗余。例如Java

List<String> listOne = Collections.emptyList();

由于类型推断和声明,将构造正确的List类型

static <T> List<T> emptyList(); 

在某些语言中,类型推断更进一步,例如在C ++中

auto p = new Person();

顺便说一句,Java语言为使用小写标识符名称(例如bob,not)设置了严格的约定Bob。这样可以避免很多歧义,例如package.Class与Class.variable。
Jerry101

...这就是为什么你有clazz
CVn

否,clazz因为class是关键字而使用,所以不能用作标识符。
Jerry101

...如果命名约定不是原样,那将不是问题。 Class是一个完全有效的标识符。
cHao 2015年

......因为是cLaSscLASScLASs
el.pescado 2015年

1

用外行的话来说:

  • 将声明与实例化分开有助于将谁使用对象与谁创建对象分离
  • 这样做时,启用了多态性,因为只要实例化类型是声明类型的子类型,所有使用该变量的代码都将起作用
  • 在强类型语言中,您必须声明一个声明其类型的变量,方法是简单地var = new Process()不首先声明该变量。

0

这也与对正在发生的事情的控制水平有关。例如,如果对象/变量的声明自动调用构造函数,

Person somePerson;

自动一样

Person somePerson = new Person(blah, blah..);

那么您将永远无法使用(例如)静态工厂方法来实例化对象而不是默认构造函数,也就是说,有时您不想为新的对象实例调用构造函数。

此示例在Joshua BlochEffective Java中进行了解释(具有讽刺意味的是,第1项!)


关于书籍,我的C#书籍和Java书籍均来自Joyce Farrell。这就是课程所指定的。我还一直在用C#和Java上的各种youtube视频作为补充。
Jason Wohlgemuth,2015年

我并不是在批评,只是提到它的来源:)
David Scholefield,2015年
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.