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?

ggrr
  • 5,725
  • 11
  • 35
  • 37
  • 6
    If we do the latter, there is no point to abstraction/generalisation anymore. Of course there are exceptions... – Walfrat Jun 01 '17 at 08: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 01 '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 01 '17 at 14:09
  • 4
    Don't use Parent/Child. Use Interface/Class (for Java). – Thorbjørn Ravn Andersen Jun 01 '17 at 15:38
  • For the record, you generally create Android Fragments via a static creation method that calls setFragmentArguments as needed, instead of using the default constructor – Selali Adobor Jun 01 '17 at 21:10
  • 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 01 '17 at 21:56
  • Possible duplicate of [Why define a Java object using interface (e.g. Map) rather than implementation (HashMap)](https://softwareengineering.stackexchange.com/questions/225674/why-define-a-java-object-using-interface-e-g-map-rather-than-implementation) – gnat Jun 02 '17 at 05:50
  • Don't use Parent/Child. Use Generalization/Specialization. – Emond Jun 02 '17 at 09:57
  • This depends **entirely** on whether or not you need the specificity of `MyFragment` in the scope in which the variable is declared. If you don't, then ultimately it makes no difference within the local scope. That said, I don't see any reason to be general when you can be specific. – Ant P Jun 02 '17 at 12:29
  • 1
    This question might be a better fit on Parenting SE https://parenting.stackexchange.com – jwg Jun 02 '17 at 13:23

10 Answers10

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.

JacquesB
  • 57,310
  • 21
  • 127
  • 176
  • 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 01 '17 at 06: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 01 '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 01 '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 01 '17 at 18:05
  • *you should declare the most abstract type possible* is a valid argument in a function that does need to know the specific type. Extending the same principle to a function where the concrete type must be known is not appropriate, IMO. That's my reason for the downvote. – R Sahu Jun 01 '17 at 21:42
  • By "most abstract", I believe you mean "widest" - that is, use a supertype in preference to a subtype. Maybe none of the classes involved will be abstract. – Dawood ibn Kareem Jun 01 '17 at 23:58
  • I would also argue for an *implicit* type, when possible. – svidgen Jun 02 '17 at 00:18
  • that's one of the worst possible example, because ArrayList and LinkedList have different complexity for basic operations, making them specialized, and seldom really interchangeable. Not to mention that in `List`, operations such as `add` are optional, so declaring it as `List` does not tell the subsequent code what it can do with your list. – njzk2 Jun 02 '17 at 04:26
  • 1
    *It depends on the context* is not an answer. – Billal Begueradj Jun 02 '17 at 06:12
  • @BillalBEGUERADJ It is the best answer when talking about programming. There isn't a best way to do anything - everything depends. – T. Sar Jun 02 '17 at 13:30
  • *everything depends.*: Einstein kept saying the same but time and science gave right to Schrödinger @TSar – Billal Begueradj Jun 02 '17 at 13:43
  • @BillalBEGUERADJ And Schrödinger said just that with his uncertainty principle. I don't understand your point. – T. Sar Jun 02 '17 at 13:44
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.

Quentin 2
  • 103
  • 2
Bernhard Hiller
  • 1,953
  • 1
  • 12
  • 17
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.

Ben
  • 853
  • 2
  • 6
  • 9
  • 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 01 '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 01 '17 at 18:00
  • +1. Also stole your example in my answer. =P – Nat Jun 02 '17 at 05: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 02 '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.

corsiKa
  • 1,084
  • 6
  • 13
  • Confused why this might get a downvote. It seems canonical, personally. Except for the idea of a programmable guard dog... – corsiKa Jun 01 '17 at 21:49
  • Why did I have to read pages, to get to the best answer? Rating sucks. – Basilevs Jun 03 '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 05 '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).

Joshua
  • 1,438
  • 11
  • 11
  • 1
    In C++, variables are mostly created without the new keyword: https://stackoverflow.com/questions/6500313/why-should-c-programmers-minimize-use-of-new – Marian Spanik Jun 02 '17 at 06: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 02 '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).

Peter
  • 3,718
  • 1
  • 12
  • 20
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.

Nat
  • 1,063
  • 1
  • 8
  • 11
  • 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 02 '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 02 '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 05 '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#](https://stackoverflow.com/questions/1014295/new-keyword-in-method-signature), and when there're multiple definitions, e.g. with [multiple-inheritance in C++](https://stackoverflow.com/questions/406081/why-should-i-avoid-multiple-inheritance-in-c). – Nat Oct 19 '17 at 03: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](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/interfaces/explicit-interface-implementation). 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 03:57
  • @corsiKa Last example would be extension methods. Because extension methods select the best overload, they can handle the same object differently based on its type, despite appearing to be object calls. So, if you call `p.doSomething();`, you could get different extension methods handling that call depending on if `p` appears to be a `Child` or `Parent`. (Plus there're weirder cases, like with reflection and such.) – Nat Oct 19 '17 at 03:59
  • @Nat so just to clarify, the reference type, and not the object type, determines the behavior? That is an absolutely terrible condition to have. Makes me glad I don't use those languages. – corsiKa Oct 19 '17 at 05:22
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.

supercat
  • 8,335
  • 22
  • 28
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.

R Sahu
  • 1,966
  • 10
  • 15
  • 3
    Being able to do less if you don't need to do more is a good thing, no? – Peter Jun 01 '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 01 '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 02 '17 at 02: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 02 '17 at 03: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 02 '17 at 09:10
  • @maaartinus, if changing `DerivedL2 x = new DerivedL2();` to `AnotherDerivedL2 x = AnotherDerivedL2();` is considered burden some, I don't have a counter argument. Re second point, I agree. But then, if you had the option of `DerivedL2 f()` and `Base f()`, which one would you use? You know my answer. – R Sahu Jun 02 '17 at 14:23
  • See my sentence concerning local variables. +++ *"which one would you use?"* - The question stands "Which one would you write?". Returning `Base` from a library means that you may later replace `DerivedL2` by something better without breaking your clients. In your own code, it's less important. – maaartinus Jun 02 '17 at 14:31
  • @maaartinus, [The answer by Nat](https://softwareengineering.stackexchange.com/a/350027/116460) explains my point better than I was able to. – R Sahu Jun 02 '17 at 14:39
  • @RSahu It's not that losing information is a good thing. It's that bogging yourself down with unnecessary information is not a good thing. The more behaviors an object has access to, the more behaviors you have to ensure are available when you go to replace that object. Imagine replacing a List with a Set: you've got potential list-only behaviors you might have to replace that you just can't do with a Set. But if you only had access to Collection, then it's literally as easy as replacing the instantiation - the rest just works. – corsiKa Jun 05 '17 at 18:57
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.

Alexander
  • 3,562
  • 1
  • 19
  • 24
  • 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 05 '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 05 '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 06 '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 06 '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` instead of a `HashMap` specifically so down the road they can change it to whatever type of `Map` they want. – corsiKa Jun 06 '17 at 17:07
  • @corsiKa I just found a real-world example that lead to a bug in production: A method returned an ImmutableMap as a Map, obscuring the fact that some operations throw exceptions. – Alexander Jun 22 '17 at 22:42
  • @corsiKa I reread what I've said here in the past, and I don't think I was clear that what I'm saying is not meant to apply "in every case". I'm saying that there are cases in which a narrower type conveys very important type information which shouldn't be discarded – Alexander Jun 22 '17 at 22:48