My Blog with stuff I am interested in. Sometimes posts are in English sometimes in Spanish.
Mi Blog con las cosas que me interesan. Algunas veces los posts estan en Ingles y otras en Español.
Now, since Java 21 the Java Team added the instance main methods feature that allows Java Programs to be launched with simple instance methods named main that no need to be static, nor public (but they cannot be private for obvious reasons), nor receive parameters:
then also they added another feature called unnamed classes that add the ability to declare classes in an implicit manner. Lets say we create a file called MyApp3.java and then in that file we write:
and soon in Java 23 every implicit class will automatically import static methods from package java.io.IO.* which will allow to write the main method much simpler.
voidmain() {
println("Hello World!");
}
but again this feature is not yet released not even as a preview.
We can use streams and collectors to get Maps from a collection of
objects. These Maps could be the result of our collection grouped or
partitioned in specific ways.
Let's say we have a collection of Employees, where an Employee has attributes
like: name, years of experience, tenure (in the company), role name,
experience level.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Now let's say we want to group our employees by the experience level:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
What if we dont need to know the employees with some experience level but the
total number of employees with that experience level?:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
In this case we are using the version of the method Collectors.groupingBy that
uses another Collector as downstream collector.
Ok, what if then, we have to partition the collection based on some condition that could be true or false? In that case we use Collectors.partitioningBy:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Collectors.partitioningBy method also has a version that receives another Collector as downstream collector where we can execute other interesting logic, like grouping the result.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Encapsulation is the ability we give our classes (and objects created
from them) to hide the implementation details of their inner workings and
control the consistency of the internal state and invariants.
It happens all the time that we expose some attribute in our class to the
outside world in the form of a collection like the following:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This is actually pretty common and I think a bad practice, we tend to create
private fields in our classes and as a reflex create setters and getters
immediately without thinking about the consequences of exposing the state that
way.
If we simply return the reference to the attribute of the object there is a
lot of danger there because now others have a copy of the reference to that
attribute that is supposed to be private and under the control of the owner
object.
Imagine if some code that gets that list of emails from our class now tries to
modify the content of that list by adding or removing some objects from it.
How can we control that? what if there is some kind of limit on the number of
emails a Contact can have? How can we ensure only valid emails are added?
What if, instead, we write the following:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Now users of our class will get a list of emails that cannot be modified. New
Email objects cannot be added or removed (but still the objects in the list
can be changed if the public methods in the Email class allow that 😉). You
could also create a copy of the list of emails by instantiating a new
ArrayList sending the list of emails as an argument to the constructor
new ArrayList<>(this.emails)
but I still think it is better to fail when trying to modify the list.
Probably it is even better to just expose public methods with just the exact
functionality that we want to allow like the following:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
With the last implementation, we are hiding more and then have even more
control. For example, let's say a Contact can have thousands of Emails (I know, it is unlikely, but let's say that is possible), then we can even change internally
the way we store Emails from a List to a Map and then be able to have better
performance for some operations, like removing an Email.
Do you have suggestions on how to improve the information hiding here? please comment!
Let's say you have a map where you store a list of values for each key, something like:
And now you want a single list with all values in the Map. Then you need to use flatMap:
Map.values() returns a collection of the values for each key in the map, since each value is a list of Strings, it will return a collection of list of strings.
But you want a single list with all strings right?
Ok, then use Stream.flatMap() method to get a stream for each list in the collection of values and then merge all lists into a single list.
Simply put, convert a Stream of Stream of values into a single list of values.