What is the advantage of instant casting?

I recently searched for a lot of code (for my own good, as I am still involved in the program), and I noticed a number of Java projects (from what seem to be respected programmers) in which they use some kind of direct casting down.

I actually have a few examples, but here is the one that I pulled directly from the code:

public Set<Coordinates> neighboringCoordinates() { HashSet<Coordinates> neighbors = new HashSet<Coordinates>(); neighbors.add(getNorthWest()); neighbors.add(getNorth()); neighbors.add(getNorthEast()); neighbors.add(getWest()); neighbors.add(getEast()); neighbors.add(getSouthEast()); neighbors.add(getSouth()); neighbors.add(getSouthWest()); return neighbors; } 

And from the same project, here is another (possibly shorter) example:

 private Set<Coordinates> liveCellCoordinates = new HashSet<Coordinates>(); 

In the first example, you can see that the method has a return type of Set<Coordinates> - however, this particular method will always return only a HashSet - and no other type of Set .

In the second example, liveCellCoordinates initially defined as Set<Coordinates> , but immediately turns into a HashSet .

And this is not only this single, specific project - I found that this takes place in several projects.

I'm curious what logic is behind this? Are there some code conventions that would consider this good practice? Does this make the program faster or more efficient? What is the use of this?

+6
source share
8 answers

When you are developing a method signature, it is usually best to just copy what you need to attach. In the first example, specifying only that the method returns Set (instead of the HashSet in particular), the developer can freely change the implementation if it turns out that the HashSet not the correct data structure. If a method were declared to return a HashSet , then all code that depended on an object, in particular a HashSet , and not a more general Set type, also needed to be revised.

A realistic example would be if it were decided that neighboringCoordinates() need to return a thread-safe Set object. As written, this would be very simple to do - replace the last line of the method:

 return Collections.synchronizedSet(neighbors); 

As it turned out, the Set object returned by synchronizedSet() is not compatible with the HashSet . Good thing the method was declared to return Set !

A similar consideration applies to the second case. Code in a class that uses liveCellCoordinates should not know anything except that it is Set . (In fact, in the first example, I would expect to see:

 Set<Coordinates> neighbors = new HashSet<Coordinates>(); 

at the top of the method.)

+12
source

Since now, if they change the type in the future, any code that depends on neighboring coordinates does not need to be updated.

Let you have:

 HashedSet<Coordinates> c = neighboringCoordinates() 

Now, let's say they change their code to use a different set implementation. Guess that you must change your code too.

But if you have:

 Set<Coordinates> c = neighboringCoordinates() 

As long as their collection still implements the set, they can change whatever they want internally without affecting your code.

In principle, this is simply the least concrete possible (within reasonable limits) for the sake of hiding internal details. Your code only cares that it can access the collection as a collection. I don't care what specific type of kit, if that makes sense. So why is your code linked to HashedSet?

+4
source

In the first example, that a method will always return only a HashSet, this is an implementation detail that users of the class should not know. This allows the developer to use a different implementation, if desired.

+2
source

The design principle in the game here is "always prefers to set abstract types."

Set is abstract; there is no such concrete class as Set ; it is an interface that, by definition, is abstract. The method contract is to return Set - it is the developer to choose how to return Set .

You must do this also with fields, for example:

 private List<String> names = new ArrayList<String>; 

not

 private ArrayList<String> names = new ArrayList<String>; 

Later, you may want to switch to using LinkedList - specifying an abstract type allows you to do this without changing the code (except for initializing the course).

+2
source

The question is how you want to use the variable. for example, is it important in your context that it is a HashSet ? If not, you have to say what you need, and this is just Set .

Everything was different if you used, for example. TreeSet here. Then you lose the information that Set sorts, and if your algorithm uses this property, changing the implementation to HashSet would be a disaster. In this case, the best solution would be to write SortedSet<Coordinates> set = new TreeSet<Coordinates>(); . Or imagine you write List<String> list = new LinkedList<String>(); : This is normal if you want to use list just like a list, but you can no longer use LinkedList as a deque, as methods like offerFirst or peekLast not in the list interface.

Thus, the general rule is to be as general as possible, but as specific as possible. Ask yourself what you really need. Does the specific interface have all the functionality and promises you need? If so, use it. Even more specific is the use of another interface or the class itself as a type.

+2
source

Here is another reason. This is because more general (abstract) types have less behavior, which is good because there are fewer opportunities to mess up.

For example, let's say you apply this method: List<User> users = getUsers(); , when in fact you could use a more abstract type: Collection<User> users = getUsers(); . Now, Bob may mistakenly assume that your method returns users in alphabetical order and creates an error. If you were to use Collection , there would be no such confusion.

+2
source

It is pretty simple.

In your example, the method returns Set . From an API designer’s point of view, this has one significant advantage over the return of a HashSet .

If at some point the programmer decides to use SuperPerformantSetForDirections , then he can do it without changing the public API if the new class extends Set .

+1
source

The trick is "code for the interface."

The reason for this is that in 99.9% of cases you just need the behavior from HashSet/TreeSet/WhateverSet match the Set interface implemented by all of them. This simplifies your code, and the only reason you really need to tell HashSet is to specify which behavior the set you need is.

As you know, a HashSet is relatively fast, but returns elements in a seemingly random order. TreeSet is a bit slower, but returns elements in alphabetical order. Your code does not care if it behaves like Set.

This provides simpler code that is easier to work with.

Note that the typical choices for Set are HashSet, Map is HashMap, and List is ArrayList. If you use a non-standard (for you) implementation, there must be a good reason for this (for example, sorting alphabetically), and this reason should be added to the comment next to the new instruction. Makes life easier for future attendants.

+1
source

Source: https://habr.com/ru/post/910323/


All Articles