1

Suppose I have the following List to hold a list of fruits.

Example:

def fruits = ["Apple", "Orange", "Grapes"]
def fruitsBowl = ["Apple", "Grapes", "Orange"]

// Will print false
println(fruits.equals(fruitBowl))

Only after I call .sort() them will both collection be equal.

fruits.sort()
fruitBowl.sort()

// Will print true
println(fruits.equals(fruitBowl))

Suppose if I had a Book class, obviously a Book class will have a list of authors. For this example, the Authors class has implemented the Comparator.

class Book {

    private String title;
    private List<Author> authors;

    Book(title, authors){
        // code to initialize left out
        authors.sort()
    }

My main concern is the equals() method. If two books are compared, even if the book objects are the same, but list of authors is unsorted, it will return false.

If I were to call .sort() the same way I call sort on fruits and fruitsBowl on the collection holding the list of authors in the Book constructor, is it bad practice?

If it's bad practice, what should I do to ensure that the equals method works?

Update:

As per the comments suppose if I used a Set, it makes much more sense, because if the books are written by a list of authors, each name will only appear once, so is doing this also considered bad practice?

class Book {

    private String title;
    private Set authorsSet;

    Book(title, authors){
       authorsSet = authors.toSet() 
    }

A Set is a collection of unique elements and the equality test will work properly, regardless of order.

  • 2
    Possible duplicate of [Legitimate "real work" in a constructor?](https://softwareengineering.stackexchange.com/questions/305464/legitimate-real-work-in-a-constructor) – gnat Aug 28 '18 at 14:56
  • just resolving my deja-vu, but why do you want to do work in the constructor? you are obvs aware of the principle, is it just that you disagree and are looking for examples where it is the correct thing to do? – Ewan Aug 28 '18 at 16:06
  • @Ewan - I don't want to work in the constructor, I just want to understand how things were done, for example, I was thinking about a book log application, to keep tracking of reading progress. From that point it got the ball rolling on other aspects, such as my Book object, how are the authors stored? In the library the books are order my author last name, can I do the same thing? If I see a feature of a program I want to understand how to works under the hood. The only way I figured out to sort the authors was in the constructor, but is that work and therefore should be avoided? –  Aug 28 '18 at 16:12
  • @Ewan - Those are the types of questions I like to ask when it comes to writing code. –  Aug 28 '18 at 16:15
  • @Sveta whatbim getting at is, is your question really 'whats the best way to represent a book with multiple authors and enable sorting by author name in my app' or 'what kinds of work are allowed in a constructor' – Ewan Aug 28 '18 at 16:20
  • @Ewan - The first one, but while thinking about it, the guideline about constructors doing work crossed my mind, and I couldn't come up with a correct answer. I've seen projects that try to open a stream in a constructor surrounded by `try/catch` obviously that's work that shouldn't be done there. Can sorting be done there? as stated, it is work. There you have my train of thought. –  Aug 28 '18 at 16:22
  • @sveta the guidline is dont do _any_ work. I would just ask question 1. question 2 is one of these divisive thing where the big evil corps are on one side and plucky genius rebel engineers like myself are fighting the good fight on the other – Ewan Aug 28 '18 at 16:32
  • @Sveta if you haven't seen it yet, check out the answers for a similar question at https://softwareengineering.stackexchange.com/questions/305464/legitimate-real-work-in-a-constructor/377605#377605 too. – zquintana Aug 28 '18 at 17:45
  • @Sveta: I can remember you asked a lot of questions around these kind of topics in the past, but looking in your profile, I cannot find any of them. What happened to them? Did you delete them all? Was your account deleted? – Doc Brown Aug 28 '18 at 18:28

3 Answers3

11

Is calling .sort() in the constructor a violation of the guideline that a constructor shouldn't do work?

Not if .sort() must be called in order to construct a valid object.

What is a valid object, you say? A valid object is one that fulfills the constraints you've imposed on it. What are those constraints? Whatever you say they are.

var sortedList = new SortedList(unsortedList);

Naturally, there are practical limits. Yesterday, someone asked a question about the Spring Framework AnnotationConfigApplicationContext class. Instantiating this class takes 15 seconds in his application, because it registers and makes available to the application a couple hundred forms. Well, 15 seconds is a pretty damn long time in computing terms, but I have applications on my computer right now that take that long to load.

This guy's problem is not the amount of work that the constructor is performing; it is that he's doing that work on every page load of a web application.

"Constructors should not do real work" is a red herring. Focus more on appropriate use.

Robert Harvey
  • 198,589
  • 55
  • 464
  • 673
  • Exactly what I was after: *Not if .sort() must be called in order to construct a valid object*. –  Aug 28 '18 at 15:12
  • See update to question. –  Aug 28 '18 at 15:41
  • 3
    I think you may be focusing too much on the idea of a "best practice." **There is no such thing as a "best practice."** There are only those practices that best meet your specific requirements. Find out what works best for your specific situation, and *do that.* – Robert Harvey Aug 28 '18 at 16:11
  • @RobertHarvey I both agree and disagree with your statement in the previous comment. There are best practices, and some people do focus too much on them. Worse then that I find people are misguided into thinking some practice is a best practice, when it's not really. In this case, I'm not sure that "Constructors should not do real work" is a best practice. That said I agree with your answer above. Especially that it's valid if it's required to make object valid. – zquintana Aug 28 '18 at 16:57
  • 3
    @zquintana A practice can be regarded as a best practice if it generally applies. Though good advice comes with a rationale, so you know when it doesn't apply; "best practices" are often marketed without such and are often in danger of being applied mindlessly everywhere regardless. – Deduplicator Aug 28 '18 at 20:23
  • @Deduplicator good points, agreed. – zquintana Oct 15 '18 at 19:23
6

The misunderstanding is that you don't have a List of authors (unless author order is important, such as in academia - but then you'd never even think of calling sort on that List), but rather you have a Set or Collection of authors.

Furthermore, the List contract specifies things that imply that it may be an unsorted list at any time. The .add() method adds to the end always.

As this is dealing with groovy, it is important to note the documentation on == and .equals() - http://groovy-lang.org/style-guide.html#_equals_and_code_code

Lists are equal if they contain the same items in the same order. - http://docs.groovy-lang.org/latest/html/groovy-jdk/java/util/List.html#equals(java.util.List)

Sets are equal if they contain the same items. http://docs.groovy-lang.org/latest/html/groovy-jdk/java/util/Set.html#equals(java.util.Set)

If you don't care about the order of the items in the Collection, don't use a List. If you are sorting the items in a List to get List equality, you don't care about the order.

user313961
  • 69
  • 1
-3

Yes it's bad practice.

sort alters the List you passed in, thanks to Java's confusing parameters by ref/value semantics.

You don't really want to get side effects from methods (or constructors)

In addition,

  1. It's good to avoid work in constructors, say I'm instantiating a million books to list by title.

  2. It's (probably) a view concern and doesn't need to be in the object

I would override the equality operator on Book to provide your custom comparison. Maybe just compare the ISBN number to avoid sorting the list. Or compare a sorted copy of the list.

Update in response to edit:

List.toSet() will remove duplicate authors. Just pass in a Set and, as a bonus, avoid work in the constructor!

Ewan
  • 70,664
  • 5
  • 76
  • 161
  • Okay fine, but how would I ensure that my `equals` method remains valid, so that if I compare two books that are the same, with an unsorted list of authors it won't return false? –  Aug 28 '18 at 15:15
  • edited, override equals for custom equality – Ewan Aug 28 '18 at 15:17
  • 1
    @sveta: To clarify the only *good* thing about this answer: you might be able to get a read on collection equality in some less expensive way than sorting a list. Having the two collections be hash tables or dictionaries would be one way of doing it. – Robert Harvey Aug 28 '18 at 15:19
  • @RH it's not really the expense which is the problem, what if i have a view which displays the authors in a different order, then I compare that book with a copy fresh out the of the DB? – Ewan Aug 28 '18 at 15:22
  • @sveta also, i have deja-vu, did you delete all your old questions? – Ewan Aug 28 '18 at 15:44
  • @Ewan - I deleted my account awhile ago, but I restored it. I never really asked question before on here anyway. –  Aug 28 '18 at 15:45
  • @Sveta maybe its someone else with the same avatar – Ewan Aug 28 '18 at 15:46
  • @Ewan - The equality will work too regardless of order. –  Aug 28 '18 at 15:58
  • @Sveta if you override it yes, if you use roberts answer.. no – Ewan Aug 28 '18 at 15:59
  • "sort alters the List you passed in, thanks to Java's confusing parameters by ref/value semantics. ... You don't really want to get side effects from methods (or constructors)" Valid point, but, **it depends** on who **owns** the List. Also, if you are instantiating a million books, sorting them is probably not the rate limiting step. – user949300 Aug 28 '18 at 16:27
  • @user949300 didnt you also comment on svetas last question? plz read my answer again, I dont think you understood. the list is passed in from outside the class and its the authors on each book you avoid sorting – Ewan Aug 28 '18 at 16:29
  • "thanks to Java's confusing parameters by ref/value semantics" do you find [it confusing in C#](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/passing-reference-type-parameters)? – JimmyJames Aug 28 '18 at 16:55
  • Why all the downvotes? –  Aug 28 '18 at 18:49
  • @sveta people dont like being told they are wrong – Ewan Aug 28 '18 at 20:09
  • @jimmyjames java is a bit different. https://www.google.co.uk/amp/s/www.javaworld.com/article/2077424/learn-java/learn-java-does-java-pass-by-reference-or-pass-by-value.amp.html – Ewan Aug 28 '18 at 20:13
  • @Ewan Java's approach for object types is identical to the semantics of the MS documentation I linked to. If you think it's different then yes, you are confused, I fail to see how C# would be less confusing since it supports the exact same semantics as well as other parameter passing modes – JimmyJames Aug 28 '18 at 20:32
  • @jimmyjames the confusing bit is if it was an array instead of a list it would have been passed by value. you can see the example code doesnt make a clear distinction between what exactly is meant. There are several articles and stack overflow questions on the subject of how confusing it is. But dont worry I get down voted for being right all the time – Ewan Aug 28 '18 at 20:39
  • @Ewan "array instead of a list it would have been passed by value." That is incorrect. Only the reference to the array is passed by value. It's still the same array object. It works the exact same way in C# if you don't mark the parameter as `ref` which isn't surprising given the lineage of C#. – JimmyJames Aug 28 '18 at 20:43
  • @jimmyjames I stand corrected! I checked 3/4 articles to make sure I was right about sort changing the passed in array. clearly it was the articles that were confusing. not java – Ewan Aug 28 '18 at 21:14
  • @Ewan The older the article the more likely it's more likely to over-complicate this. Your major point about sorting the array in place is right on. It's not a great practice to sort things that belong to other parts of the code. Not unless it's very clear that this is part of it's functionality. Not something I would do in a constructor. – JimmyJames Aug 28 '18 at 21:21