Laz
Laz

Reputation: 3538

When is it Appropriate to use Generics Versus Inheritance?

What are the situations and their associated benefits of using Generics over Inheritance and vice-versa, and how should they be best combined?

Thanks for the answer guys.

I'm going to try to state the motivation for this question as best I can: I have a class as shown below:

class InformationReturn<T> where T : Info
{
    InformationReturn(Employee employee, List<T>) { ... }
}

Now suppose I have a repository that takes an InformationReturn argument, that has to strore different fields in a DB depending on the type of Info object T is. Is it better to create different repositories each for the type T is; one repository that uses reflection to determine the type; or is there a better way using inheritance capabilities over/with generics?

Note: other client code must operate differently based on the type of T as well.

Upvotes: 23

Views: 13050

Answers (8)

alaboudi
alaboudi

Reputation: 3413

Late to the party, but I have more value to add to this question.

Generics are used when you want to maintain the same implementation while providing a different type definition to clients. Consider the following:

const rabbitPopulation = new Population<Rabbit>();
const mousePopulation = new Population<Mouse>();

rabbitPopulation.add(rabbit) // ✅ will pass
mousePopulation.add(rabbit) // ❌ will not compile

both rabbitPopulation and mousePopulation have have the same method implementations while differing only by type signatures. When you model your code this way, you are implicitly saying that all "populations" should behave and act similarly.

Meanwhile, inheritance is used to extend or overwrite the implementation altogether.

class RabbitPopulation extends Population {
  increase() { /* increase rabbit population in a specific way */}
}
const rabbitPopulation = new RabbitPopulation();

class MousePopulation extends Population {
  increase() { /* increase mouse population in a specific way */}
}
const mousePopulation = new MousePopulation();

As you can see, both rabbitPopulation and mousePopulation can now have their own implementations of similar methods. This is useful when want to capture a semantic idea across two objects (i.e both are populations) while allowing them to differ in behaviors (they have different methods on each).

You can use both together to express your modelling in a very rich way by making the base class a generic. Consider the following:

class MousePopulation extends Population<Mouse> {/**/}

👆 This way you can give similar implementations to basic functionalities across all semantic ideas of a "population" while providing specific implementations for subtypes of a population (i.e. MousePopulation) in a type rich way. But most times when you do this, you almost always want the Population<T> to be extended and might as well benefit from making your the Population class an abstract one.

abstract class Population<T> {}
class MousePopulation extends Population<Mouse> {}

I prefer this setup because you allow common semantic ideas to be captured in a base class while allowing specific functionalities to be extended by subclasses

Upvotes: 1

C. Ross
C. Ross

Reputation: 31848

You should use generics when you want only the same functionality applied to various types (Add, Remove, Count) and it will be implemented the same way. Inheritance is when you need the same functionality (GetResponse) but want it to be implemented different ways.

Upvotes: 36

Amer Jamaeen
Amer Jamaeen

Reputation: 766

Neither inheritance nor parameterized types can change at run-time

Inheritance lets you provide default implementations for operations and lets subclasses override them.

Parameterized types let you change the types that a class can use.

But Which approach is best depends on your design and implementation constraints

Design Patterns Elements of Reusable Object-Oriented Software

Upvotes: 0

Robin Vessey
Robin Vessey

Reputation: 4639

Use generics when you want to create a "template" that can apply to many styles of unknown classes. Eg. Collections holding ??? are good candidates for generics. Inheritance is for when you have a base conecpt that children "are" an extension of that concept.

My general rules

  • if you start defining properites as Object in your code and type casting a lot, then its probably time for generics.
  • If you want to program against the "higher concept" as well as the derived class then its inheritance.
  • If your class is going to wrap or work on a concerte class then its generics.
  • If you are relying on the specifics of an unknown "yet to be defined" class then your probably better with generics.
  • If you derived classes are "is a" then its usually inheritance
  • If your superclass "uses some x" then its generics.

Upvotes: 7

Jon Skeet
Jon Skeet

Reputation: 1500455

Use generics to specify an algorithm or type's behaviour which can be expressed in terms of some "unknown type" while keeping an API which is strongly typed in terms of that unknown type. The unknown type is known as a type parameter and is expressed in the code like this:

public class List<T>
{
    public void Add(T item)
}

(etc) - here T is the type parameter. Generic methods are similar:

public void Foo<T>(T item)

The calling code specifies the type argument it wants to work with, e.g.

List<string> list = new List<string>();
list.Add("hi");

Use inheritance to specialize the behaviour of a type.

I can't really think of many places where they're alternatives to each other...

Upvotes: 11

Adam Robinson
Adam Robinson

Reputation: 185643

They're really different ideas altogether. Generics allow you to declare common "specific" functionality (at the risk of sounding oxymoronic) in a general way. A List<int> doesn't function any differently from a List<string>, aside from the type of data that is held inside.

While inheritance could be used to do the same thing, I could create a List class, then an IntList and a StringList class that inherit from it. I could easily make these two classes function entirely differently, or have one offer functionality not available in the other one.

Edit

After reviewing the edit to the question, the answer sortof is "it depends." You can make arguments for both sides--and, indeed, LINQ to SQL and the Entity Framework are both a combination of both reflective inspection using generics, and strongly-typed entity classes and other repository classes. You can certainly take the approach that you're looking at, just know that, in general, a problem that can be solved either with reflection or with something else is LIKELY going to be faster when solved by the "something else." It comes down to how much of a tradeoff you want among performance, maintainability, readability, and reliability.

Upvotes: 13

Andrew Hare
Andrew Hare

Reputation: 351506

Generics and inheritance are two separate things. Inheritance is an OOP concept and generics are a CLR feature that allows you specify type parameters at compile-time for types that expose them.

Inheritance and generics actually work quite well together.

Inheritance:

Inheritance allows me to create one type:

class Pen { }

and then later create another type that extends Pen:

class FountainPen : Pen { }

This is helpful because I can reuse all the state and behavior of the base class and expose any new behavior or state in FountainPen. Inheritance allows me to rapidly create a more specific version of an existing type.

Generics:

Generics are a CLR feature that let me create a type like this:

class Foo<T> 
{
    public T Bar { get; set; }
}

Now when I use Foo<T> I can specify what the type of T will be by providing a generic type argument:

Foo<int> foo = new Foo<int>();

Now, since I have specified that T shall be an int for the object I have just created, the type of Foo.Bar will also be int since it was declared of type T.

Upvotes: 13

Brian Genisio
Brian Genisio

Reputation: 48127

Inheritance is much more about "is-a" (frog is an animal) where generics is about creating containers that act on typed data (List of T, Processor of T, etc). They are not mutually exclusive.

You can have this if you want:

public class Base<T>
{

}

public class Derived : Base<Foo>
{

}

Upvotes: 4

Related Questions