32

For example,I had seen some codes that create a fragment like this:

Fragment myFragment=new MyFragment();

which declares a variable as Fragment instead of MyFragment , which MyFragment is a child class of Fragment. I'm not satisified this line of codes because I think this code should be:

MyFragment myFragment=new MyFragment();

which is more specific, is that true?

Or in generalization of the question, is it bad practice to use:

Parent x=new Child();

instead of

Child x=new Child();

if we can change the former one into latter one without compile error?

  • 6
    If we do the latter, there is no point to abstraction/generalisation anymore. Of course there are exceptions... – Walfrat Jun 1 '17 at 8:44
  • 2
    In a project I'm working on, my functions expect the parent type, and runs parent type methods. I can accept any of several children, and the code behind those (inherited and overridden) methods is different, but I want to run that code no matter which child I am using. – Stephen S Jun 1 '17 at 13:40
  • 4
    @Walfrat Indeed there is little point in abstraction when you are already instanting the concrete type, but you can still return the abstract type so that the client code is blissfully ignorant. – Jacob Raihle Jun 1 '17 at 14:09
  • 4
    Don't use Parent/Child. Use Interface/Class (for Java). – Thorbjørn Ravn Andersen Jun 1 '17 at 15:38
  • 4
    Google "programming to an interface" for a bunch of explanations on why using Parent x = new Child() is a good idea. – Kevin Workman Jun 1 '17 at 21:56

10 Answers 10

69

It depends on the context, but I would argue you should declare the most abstract type possible. That way your code will be as general as possible and not depend on irrelevant details.

An example would be having a LinkedList and ArrayList which both descend from List. If the code would work equally well with any kind of list then there is no reason to arbitrary restrict it to one of the subclasses.

  • 24
    Exactly. To add an example, think about List list = new ArrayList(), The code using the list doesn't care what kind of list it is, only that meets the contract of the List interface. In the future, we could easily change to a different List implementation and the underlying code wouldn't need changing or updating at all. – Maybe_Factor Jun 1 '17 at 6:12
  • 19
    good answer but should expand that most abstract type possible means that the code in scope should not be casting back to the child to perform some child specific operation. – Mike Jun 1 '17 at 14:08
  • 10
    @Mike: I hope that goes without saying, otherwise all variables, parameters and fields would be declared as Object! – JacquesB Jun 1 '17 at 14:45
  • 3
    @JacquesB: since this question and answer received so much attention I was thinking that being explicit would be useful for people with all different skill levels. – Mike Jun 1 '17 at 18:05
  • 1
    It depends on the context is not an answer. – Billal Begueradj Jun 2 '17 at 6:12
11

JacquesB's answer is correct, though a little abstract. Let me emphasize some aspects more clearly:

In your code snippet, you explicitly use the new keyword, and perhaps you would like to continue with further initialization steps which are likely specific for the concrete type: then you need to declare the variable with that specific concrete type.

The situation is different when you get that item from somewhere else, e.g. IFragment fragment = SomeFactory.GiveMeFragments(...);. Here, the factory should normally return the most abstract type possible, and the downward code should not depend on implementation details.

  • 18
    "though a little abstract". Badumtish. – rosuav Jun 1 '17 at 10:02
  • 1
    couldn't this have been an edit? – beppe9000 Jun 1 '17 at 17:10
6

There are potentially performance differences.

If the derived class is sealed, then the compiler can inline and optimise or eliminate what would otherwise be virtual calls. So there can be a significant performance difference.

Example: ToString is a virtual call, but String is sealed. On String, ToString is a no-op, so if you declare as object that's a virtual call, if you declare a String the compiler knows no derived class has overridden the method because the class is sealed, so that's a no-op. Similar considerations apply to ArrayList vs LinkedList.

Therefore, if you know the concrete type of the object, (and there is no encapsulation reason to conceal it), you should declare as that type. Since you have just created the object, you know the concrete type.

  • 4
    In most cases, the performance differences are minuscule. And in most cases (I've heard it said that this is about 90%of the time) we should forget about those small differences. Only when we know (preferably via measurement) that we age in as performance critical section of code should we care about such optimizations. – Jules Jun 1 '17 at 16:52
  • And the compiler knows that "new Child()" returns a Child, even when assigned to a Parent, and as long as it knows, it can use that knowledge and call the Child() code. – gnasher729 Jun 1 '17 at 18:00
  • +1. Also stole your example in my answer. =P – Nat Jun 2 '17 at 5:53
  • 1
    Note that there are performance optimisations that make the whole thing moot. HotSpot for example will notice that a parameter passed to a function is always of one specific class and optimise accordingly. If that assumption ever turns out to be false the method is deoptimized. But this strongly depends on the compiler/runtime environment. Last time I checked the CLR couldn't even do the rather trivial optimisation of noticing that p from Parent p = new Child() is always a child.. – Voo Jun 2 '17 at 13:27
4

The key difference is the level of access required. And every good answer about abstraction involves animals, or so I'm told.

Let us suppose you have a few animals - Cat Bird and Dog. Now, these animals have a few common behaviors - move(), eat(), and speak(). They all eat differently and speak differently, but if I need my animal to eat or speak I don't really care how they do it.

But sometimes I do. I've never hard a guard cat or a guard bird, but I do have a guard dog. So when someone breaks into my house, I can't just rely on speak to scare away the intruder. I really need my dog to bark - this is different than his speak, which is just a woof.

So in code that requires an intruder I really need to do Dog fido = getDog(); fido.barkMenancingly();

But most of the time, I can happily have any animals do tricks where they woof, meow or tweet for treats. Animal pet = getAnimal(); pet.speak();

So to be more concrete, ArrayList has some methods that List doesn't. Notably, trimToSize(). Now, in 99% of cases, we don't care about this. The List is almost never big enough for us to care. But there are times when it is. So occasionally, we must specifically ask for an ArrayList to execute trimToSize() and make its backing array smaller.

Note that this does not have to be made at construction - it could be done via a return method or even a cast if we're absolutely sure.

  • Confused why this might get a downvote. It seems canonical, personally. Except for the idea of a programmable guard dog... – corsiKa Jun 1 '17 at 21:49
  • Why did I have to read pages, to get to the best answer? Rating sucks. – Basilevs Jun 3 '17 at 13:09
  • Thanks for the kind words. There are pros and cons to the rating systems, and one of those is that later answers can sometimes get left in the dust. It happens! – corsiKa Jun 5 '17 at 18:51
2

Generally speaking, you should use

var x = new Child(); // C#

or

auto x = new Child(); // C++

for local variables unless there is a good reason for the variable to have a different type as what it is initialized with.

(Here I'm ignoring the fact that the C++ code should most likely be using a smart pointer. There are cases not to and without more than one line I can't actually know.)

The general idea here is with languages that support automatic type detection of variables, using it makes the code easier to read and does the right thing (that is, the compiler's type system can optimize as much as possible and changing the initialization to be a new type works as well if most abstract had been used).

  • 1
    In C++, variables are mostly created without the new keyword: stackoverflow.com/questions/6500313/… – Marian Spanik Jun 2 '17 at 6:08
  • 1
    The question is not about C#/C++ but about a general object oriented issue in a language that has at least some form of static typing (i.e., it would be nonsensical to ask in something like Ruby). Besides, even in those two languages, I don't really see a good reason for why we should do it like you say. – AnoE Jun 2 '17 at 15:00
1

Good because it's easy to understand and easy to modify this code:

var x = new Child(); 
x.DoSomething();

Good because it communicates intent:

Parent x = new Child(); 
x.DoSomething(); 

Ok, because it's common and easy to understand:

Child x = new Child(); 
x.DoSomething(); 

The one option that is truly bad is to use Parent if only Child has a method DoSomething(). It's bad because it communicates intent wrongly:

Parent x = new Child(); 
(x as Child).DoSomething(); // DON'T DO THIS! IF YOU WANT x AS CHILD, STORE x AS CHILD

Now let's elaborate a bit more on the case the question specifically asks about:

Parent x = new Child(); 
x.DoSomething(); 

Let's format this differently, by making the second half a function call:

WhichType x = new Child(); 
FunctionCall(x);

void FunctionCall(WhichType x)
    x.DoSomething(); 

This can be shortened to:

FunctionCall(new Child());

void FunctionCall(WhichType x)
    x.DoSomething();

Here, I assume it's widely accepted that WhichType should be the most basic/abstract type that allows the function to work (unless there are performance concerns). In this case the proper type would be either Parent, or something Parent derives from.

This line of reasoning explains why using the type Parent is a good choice, but it doesn't go into weather the other choices are bad (they are not).

1

tl;dr- Using Child over Parent is preferable in the local scope. It not only helps with readability, but it's also necessary to ensure that overloaded method resolution works properly and helps enable efficient compilation.


In the local scope,

Parent obj = new Child();  // Works
Child  obj = new Child();  // Better
var    obj = new Child();  // Best

Conceptually, it's about maintaining the most type information possible. If we downgrade to Parent, we're essentially just stripping out type information that could've been useful.

Retaining the complete type information has four main advantages:

  1. Provides more information to the compiler.
  2. Provides more information to the reader.
  3. Cleaner, more standardized code.
  4. Makes the program logic more mutable.

Advantage 1: More information to the compiler

The apparent type is used in overloaded method resolution and in optimization.

Example: Overloaded method resolution

main()
{
    Parent parent = new Child();
    foo(parent);

    Child  child  = new Child();
    foo(child);
}
foo(Parent arg) { /* ... */ }  // More general
foo(Child  arg) { /* ... */ }  // Case-specific optimizations

In the above example, both foo() calls work, but in one case, we get the better overloaded method resolution.

Example: Compiler optimization

main()
{
    Parent parent = new Child();
    var x = parent.Foo();

    Child  child  = new Child();
    var y = child .Foo();
}
class Parent
{
    virtual         int Foo() { return 1; }
}
class Child : Parent
{
    sealed override int Foo() { return 2; }
}

In the above example, both .Foo() calls ultimately call the same override method that returns 2. Just, in the first case, there's a virtual method lookup to find the correct method; this virtual method lookup isn't needed in the second case since that method's sealed.

Credit to @Ben who provided a similar example in his answer.

Advantage 2: More information to the reader

Knowing the exact type, i.e. Child, provides more information to whoever's reading the code, making it easier to see what the program's doing.

Sure, maybe it doesn't matter to the actual code since both parent.Foo(); and child.Foo(); make sense, but for someone seeing the code for the first time, more information's just plain helpful.

Additionally, depending on your development environment, the IDE may be able to provide more helpful tooltips and metadata about Child than Parent.

Advantage 3: Cleaner, more standardized code

Most of the C# code examples that I've seen lately use var, which is basically shorthand for Child.

Parent obj = new Child();  // Sub-optimal
Child  obj = new Child();  // Optimal, but anti-pattern syntax
var    obj = new Child();  // Optimal, clean, patterned syntax "everyone" uses now

Seeing a non-var declaration statement just looks off; if there's a situational reason for it, awesome, but otherwise it looks anti-pattern.

// Clean:
var foo1 = new Person();
var foo2 = new Job();
var foo3 = new Residence();

// Staggered:
Person foo1 = new Person();
Job foo2 = new Job();
Residence foo3 = new Residence();   

Advantage 4: More mutable program logic for prototyping

The first three advantages were the big ones; this one's far more situational.

Still, for people who use code like others use Excel, we're constantly changing our code. Maybe we don't need to call a method unique to Child in this version of the code, but we might repurpose or rework the code later.

The advantage of a strong type system is that it provides us with certain meta-data about our program logic, making the possibilities more readily apparent. This is incredibly useful in prototyping, so it's best to keep it where possible.

Summary

Using Parent messes up overloaded method resolution, inhibits some compiler optimizations, removes information from the reader, and makes the code uglier.

Using var is really the way to go. It's quick, clean, patterned, and helps the compiler and IDE to do their job properly.


Important: This answer is about Parent vs. Child in a method's local scope. The issue of Parent vs. Child is very different for return types, arguments, and class fields.

  • 2
    I would add that Parent list = new Child() doesn't make much sense. The code that directly creates the concrete instance of an object (as opposed to indirection through factories, factory methods etc.) has perfect information about the type being created, it might need to interact with the concrete interface to configure the newly created object etc. Local scope is not where you achieve flexibility. Flexibility is achieved when you expose that newly created object to a client of your class using a more abstract interface (factories and factory methods are a perfect example) – crizzis Jun 2 '17 at 10:22
  • "tl;dr Using Child over Parent is preferable in the local scope. It not only helps with readability," - not necessarily... if you make no use of the child specifics, then it will just add more details than necessary. "but it's also necessary to ensure that overloaded method resolution works properly and helps enable efficient compilation." - that depends a lot on the programming language. There are plenty that have no issue whatsoever arising from this. – AnoE Jun 2 '17 at 14:58
  • 1
    Do you have a source that Parent p = new Child(); p.doSomething(); and Child c = new Child; c.doSomething(); have different behavior? Maybe that's a quirk in some language, but it's supposed to be that the object controls the behavior, not the reference. That's the beauty of inheritance! As long as you can trust child implementations to fulfill the contract of the parent implementation you're good to go. – corsiKa Jun 5 '17 at 18:54
  • @corsiKa Yeah, it's possible in a few ways. Two quick examples would be where method signatures are overwritten, e.g. with the new keyword in C#, and when there're multiple definitions, e.g. with multiple-inheritance in C++. – Nat Oct 19 '17 at 3:46
  • @corsiKa Just a quick third-example (because I think it's a neat contrast between C++ and C#), C# has an issue like C++'s, where you can also get this behavior from explicit-interface methods. It's funny because languages like C# use interfaces instead of multiple-inheritance in-part to avoid these issues, but by adopting interfaces, they still got part of the issue - but, it's not quite as bad because, in that scenario, it applies only to when Parent is an interface. – Nat Oct 19 '17 at 3:57
0

Given parentType foo = new childType1();, code will be limited to using parent-type methods on foo, but will be able to store into foo a reference to any object whose type derives from parent--not just instances of childType1. If the new childType1 instance is the only thing to which foo will ever hold a reference, then it may be better to declare foo's type as childType1. On the other hand, if foo might need to hold references to other types, having it declared as parentType will make that possible.

0

I suggest using

Child x = new Child();

instead of

Parent x = new Child();

The latter loses information while former does not.

You can do everything with the former that you can do with the latter but not vice versa.


To elaborate the pointer further, let's you have multiple levels of inheritance.

Base -> DerivedL1 -> DerivedL2

And you want to initialize the result of new DerivedL2() to a variable. Using a base type leaves you the option of using Base or DerivedL1.

You can use

Base x = new DerivedL2();

or

DerivedL1 x = new DerivedL2();

I don't see any logical way to prefer one over the other.

If you use

DerivedL2 x = new DerivedL2();

there is nothing to argue about.

  • 3
    Being able to do less if you don't need to do more is a good thing, no? – Peter Jun 1 '17 at 21:36
  • 1
    @Peter, the ability to do less is not constrained. It is always possible. If the ability to do more is constrained, it might be a sore point. – R Sahu Jun 1 '17 at 21:39
  • But losing information is sometimes a good thing. Concerning you hierarchy, most people would probably vote for Base (assuming it works). You prefer the most specific, AFAIK the majority prefers the least specific. I'd say that for local variables it hardly matters. – maaartinus Jun 2 '17 at 2:22
  • @maaartinus, I am surprised the majority of users prefer losing information. I am not seeing the benefits of losing information as clearly as others are. – R Sahu Jun 2 '17 at 3:46
  • When you declare Base x = new DerivedL2(); then you are constrained most and this allows you to switch another (grand-)child in with minimum effort. Moreover, you see immediately that it's possible. +++ Do we agree that void f(Base) is better than void f(DerivedL2)? – maaartinus Jun 2 '17 at 9:10
0

I would suggest always returning or storing variables as the most specific type, but accepting parameters as the broadest types.

E.g.

<K, V> LinkedHashMap<K, V> keyValuePairsToMap(List<K> keys, List<V> values) {
   //...
}

The parameters are List<T>, a very general type. ArrayList<T>, LinkedList<T> and others could all be accepted by this method.

Importantly, the return type is LinkedHashMap<K, V>, not Map<K, V>. If somebody wants to assign the result of this method to a Map<K, V>, they can do that. It clearly communicates that this result is more than just a map, but a map with a defined ordering.

Suppose this method returned Map<K, V>. If the caller wanted a LinkedHashMap<K, V>, they would have to do a type check, cast and error handling, which is really cumbersome.

  • The same logic that forces you to accept a broad type as a parameter should also apply to the return type as well. If the goal of the function is to map keys to values, it should return a Map not a LinkedHashMap - you would only want to return a LinkedHashMap for a function like keyValuePairsToFifoMap or something. Broader is better until you have a specific reason not to. – corsiKa Jun 5 '17 at 19:00
  • @corsiKa Hmm I agree that's a better name, but this is just one example. I disagree that (in general) broadening your return type is better. Your artificially removing type information, without any justification. If you envision your user will reasonably need to cast back down the broad type you've provided them, then you've done them a disservice. – Alexander Jun 5 '17 at 19:05
  • "without any justification" - but there is justification! If I can get away with returning a broader type it makes refactoring easier down the road. If someone NEEDS the specific type because of some behavior on it, I'm all for returning the appropriate type. But I don't want to be locked into a specific type if I don't have to me. I'd like to be free to alter the specifics of my method without harming those that consume it, and if I change it from, say LinkedHashMap to TreeMap for whatever reason, now I have a lot of stuff to change. – corsiKa Jun 6 '17 at 16:06
  • Actually, I agree with that all the way up until the last sentence. You changed from LinkedHashMap to TreeMap isn't a trivial change, such as changing from ArrayList to LinkedList. It's a change with semantic importance. In that case you want the compiler to throw an error, to force consumers of the return value to notice the change and take appropriate action. – Alexander Jun 6 '17 at 16:54
  • But it can be a trivial change if all you care about is map behavior (which, if you can, you should.) If the contract is "you will get a key value map" and order doesn't matter (which is true for most maps) then it doesn't matter if it's a tree or hash map. For example, Thread#getAllStackTraces() - returns a Map<Thread,StackTraceElement[]> instead of a HashMap specifically so down the road they can change it to whatever type of Map they want. – corsiKa Jun 6 '17 at 17:07

protected by gnat Jun 2 '17 at 5:44

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.