DEV Community

Max Cerrina
Max Cerrina

Posted on

ELI5: Why cast to an interface?

So, I am super new to OOP and Java--specifically, just finishing up my first semester of it.

I understand interfaces are akin to contracts, and understand the uses of them; similarly, I understand casting, etc. I understand you can cast to an interface, but I don't get why you would want to. I've checked a number of books, a number of sites, read some arguments on the topic on SO, and have yet to actually get the point of doing so.

Specifically: if you had an interface called Forecastable and two classes, Weather and StockMarket, both of which implemented Forecastable--and thus must have the methods of the interface, and thus any object instantiated in them has access to those methods by definition--then what would the actual use be of casting either some Weather or StockMarket object be to Forecastable, if they can both already access those methods inherently?

Top comments (11)

Collapse
 
mortoray profile image
edA‑qa mort‑ora‑y

There are only a few situations where I've explicit cast to an interface:

  • I am downcasting from a less-specified type, such as object or a base class. That is, an implicit cast doesn't work in these situations.
  • I'm trying to call an interface function in C# which is exposed only on the interface (it's a C# oddity)
  • I'm forcing selection of a function overload where multiple might work (such as when a class implements multiple interfaces and both have functions). This is a hack and usually indicates an API defect.

The idea of casting to an explicit interface just to limit the interface, used locally in a function, seems like an anti-pattern to me: the function is mixing multiple divergent purposes. If you have the full object then you can use it. If you're making generic code then put it in a function that takes only the interface.

Collapse
 
lluismf profile image
Lluís Josep Martínez

I agree 100% it's an anti-pattern.

Collapse
 
evanoman profile image
Evan Oman • Edited

Casting to an interface allows you to pass/refer to objects with the same behavior.

For example, if you wanted to make a report from a bunch of forecasts you could write code like this:

/* Collect some forecastable things */
List<Forecastable> forecastables = Arrays.asList(nycWeather,frankfurtWeather, nyse, dax);

/* Get the forecasts themselves */
List<Forecast> forecasts = forecastables.stream()
    .map(Forecastable::getForecast)
    .collect(Collectors.toList());

/* Add the forecasts to our report */
report.addForecasts(forecasts);

Here you can see that we only need the Forecastable subset of these objects' functionality so we cast them to the interface.

Collapse
 
hamzaop profile image
Hamza • Edited

If you cast to Forecastable, then that object can only access the methods of the interface, you can't access other methods in the Weather or StockMarket class.

Collapse
 
alephnaught2tog profile image
Max Cerrina

Okay, that makes sense. But why would you want to do that?

Collapse
 
dubyabrian profile image
W. Brian Gourlie • Edited

One reason is that you want to ensure that no methods that aren't part of the contract are called on the object. If the interface represents a contract, yet you can call a method that isn't on the interface, in a sense, the contract has been broken!

I will say that it's somewhat odd to cast a type to accomplish this. Typically you would either instantiate an object of the interface type, for example:

MyContract contract = new MyContractImpl();

Or, you make sure your methods accept only the contract, and not the implementation:

void doSomething(MyContract contract) {
    ...
}

This way, even though the underlying type is a MyContractImpl, you won't be able to call methods on it that aren't a part of MyContract!

Collapse
 
hamzaop profile image
Hamza

You can do that for example to check whether some class implements an interface at runtime with a try catch block, if the cast fails then you can throw an error saying that the class should implement the interface, one example is to make a callback between Activity and Fragment in Android.

Thread Thread
 
lluismf profile image
Lluís Josep Martínez

This would be a programming error and you shouldn't use the Exception mechanism to catch errors.

Collapse
 
florianschaetz profile image
(((Florian Schätz)))

Every point of code should only see the minimum amount of stuff it needs to see. The less it sees, the less complexity it has to deal with and thus it itself will probably less complex.

This is not only trivially true for methods, where using the interface as a parameters also makes it a lot more generic, but even when designing interfaces: Implementing multiple smaller interfaces are preferable over big composite ones, because if your interfaces are big, the code sees a lot of stuff that it doesn't actually need. With multiple smaller interfaces, the code can work with the one that it actually needs and doesn't even see the other stuff. This is, I believe, called the interface segregation principle (see en.wikipedia.org/wiki/Interface_se...).

Where does this lead? It leads us to almost automatically casting to the interface. When we write something like this...

Weather weather = new Weather();

...we immediately ask ourselves: "Does the code that follows actually need the complete Weather? Or will all following code work fine with a Forecastable?" In the later case, we can immediately replace it with...

Forecastable forecastable = New Weather();

...since we know that we don't actually care that it's Weather. Of course, if we do care, we need to work with Weather - but perhaps we can return it as a humble Forcastable, because nobody outside the method creating it actually cares?

Collapse
 
miffpengi profile image
Miff

I don't really see a reason to explicitly downcast to an interface, but there are two situations I see about casting to an interface: when upcasting (when you don't know in advance if the object you're working with implements the interface) and the second is implicit casting (when you're calling code that operates on the interface only).

Collapse
 
lluismf profile image
Lluís Josep Martínez

When you invoke a legacy method that returns Object or a very bad designed API. In these cases if you can't refactor the method (if it's an old version of Hibernate for instance) you have no other option than casting. I use to create wrappers over this kind of methods to generify them. Once they're generified you can remove all the casts.