Thứ Hai, 23 tháng 11, 2009

Java Collections Default Implementation Choices: Groovy's Opinion

I think many Java developers use the Java Collections Framework like I do: start with the same basic implementations of each of the major collections interfaces for general cases and only use a different implementation when there is a need that drives it (such as need for NavigableMap). For example, for a long time, I used ArrayList for Lists, HashSet for Sets, and HashMap for Maps. I have started to heavily use EnumSet and EnumMap for appropriate Set and Map situations respectively, but I still seem to have my same favorite "default" collections implementations.

Ruby on Rails in large part made "convention over configuration" popular and the Java Peristence API (JPA) further popularized this concept in the Java world under the name "configuration by exception." Of course, many other frameworks and libraries have similarly used and popularized the concept of reducing the need for explicit configuration when conventions and defaults are good enough. One of the appealing facets of Groovy is its support for certain collection types by default without explicit specification and only assumed from the syntax used. In this post, I look at the default implementations that the Groovy developers selected for the Groovy language to use when no specific implementation is specified.

Most introductory Groovy materials demonstrate (and often applaud) the conciseness and intuitiveness of Groovy's collections specification syntax. Much of this is a result of Groovy assuming which implementation of each collection will be used. Groovy does allow this default choice to be overridden by explicit specification of the desired collection implementation. The following code snippet demonstrates Groovy's default implementations for Lists, Sets, and Maps.


list = ["Denver", "Chicago", "Tallahassee", "Detroit", "Salt Lake City", "Las Vegas", "Atlanta"]
println "\nLIST: Default List implementation is ${list.getClass().name}\n"

map = ["Colorado" : "Denver", "Florida" : "Tallahassee", "Michigan" : "Detroit"]
println "MAP: Default Map implementation is ${map.getClass().name}\n"

set1 = list as Set
println "SET: Default Set implementation is ${set1.getClass().name}\n"

Set set2 = ["Colorado", "Florida", "Michigan", "Utah", "Nevada", "Georgia"]
println "SET: Default Set implementation is ${set2.getClass().name}\n"

queue = list as Queue
println "QUEUE: Default Queue implementation is ${queue.getClass().name}\n"


When the above script is run (as demonstrated in the next screen snapshot), we learn that the ArrayList is Groovy's default implementation of the List interface, that HashSet is Groovy's default implementation of the Set interface, LinkedHashMap is Groovy's default implementation of choice for the Map interface, and LinkedList is Groovy's default implementation of the Queue interface.



The authors of the book Java Generics and Collections compare the standardly supplied Java collections implementations in their book. They devote sections of their book (13.3/Sets, 15.3/Lists, 16.6/Maps, 14.5/Queues) to comparing different collections implementations for each interface (List, Set, and Map) and compare them based on comparative performance and functional requirements they satisfy. There are so many implementations and so many considerations facing one over another that it is difficult to choose one to "generally use," but the Groovy selections definitely seem reasonable based on the analysis contained in this book. One obvious exception to this is that the authors are critical (Section 14.4.1) of LinkedList as a Queue (or even Deque) implementation.

Conclusion

Groovy provides concise and highly intuitive syntax for some of its collections implementations, but must make certain assumptions about the particular collection implementation to do this. It is interesting to compare the default implementation choices for Groovy use of collections interfaces to my own default choices. Of course, when in a concurrent environment or facing certain other unique circumstances, the idea of a "general" collection implementation doesn't apply anymore anyway. Certain collections implementations may also be favored for different types of operations on the collection.

Thứ Bảy, 21 tháng 11, 2009

Using Groovy to Check Seventh Grade Homework

Using Groovy to Calculate Volume and Surface Area of a Cylinder

I have recently been involved in checking a seventh grader's math homework to make sure it was done correctly and to help her learn the new concepts. The recent focus of her class has been on volumes and surface areas of various geometric shapes. These can be tedious to check the arithmetic on. Although I do have a HP48GX calculator (my HP48SX got stolen about 3/4 of the way through my degree) from the days of my undergraduate degree in electrical engineering, its built-in formulas use a definition of pi with many more significant digits than the seventh graders use (3.14). There are other options available (such as Google search's online calculator function), but the potentially different precision of PI is still an issue.

For a variety of reasons (inner geekiness, long-term laziness, and because I can use it as a blog topic) I decided to write a small application to make these checks easier. I did not want to directly deal with floating-point arithmetic issues and Java's handy BigDecimal came to mind. At about the same time, I realized that this was a perfect use for Groovy: I wanted a Java language feature but wanted to be able to write concise, script code.

The volume (V = Πr2h) and surface area (SA = 2Πr2 + 2Πrh) of a cylinder are the main topics for the current homework assignment. The Groovy code below calculates the volume and surface area of a cylinder given a radius as the first parameter and a height as the second parameter. It is assumed that the radius and height are in the same (unspecified) units.


// cylindrical1.groovy
//
// Calculates geometric characteristics of a cylinder with given radius and height.
//

// Don't want to use Math.PI because want same accuracy as used in homework
pi = 3.14

radius = new BigDecimal(args[0])
height = new BigDecimal(args[1])

volume = pi * radius * radius * height
surfaceArea = 2 * pi * radius * radius + pi * 2 * radius * height

println "Volume of cylinder with radius ${radius} and height ${height} is ${volume}."
println "Surface area of cylinder with radius ${radius} and height ${height} is ${surfaceArea}"


Running the script on several different values for radius and height of different cylinders is shown in the next screen snapshot.



The script above does the job, but it can be improved even more. In the second version of this script, I add a conditional check that prints out usage information if the caller does not provide two numeric values for radius and height. I also use Groovy's GDK-extended String method toBigDecimal() rather than the more traditional approach of passing the String to the BigDecimal constructor.


// cylindrical2.groovy
//
// Calculates geometric characteristics of a cylinder with given radius and height.
//

if (args.length < 2)
{
println """You must enter at least two numeric parameters for radius and height.
Usage: cylindrical2 <<radius>> <<height>>"""
System.exit(args.length)
}

// Don't want to use Math.PI because want same accuracy as used in homework
pi = 3.14

radius = args[0].toBigDecimal()
height = args[1].toBigDecimal()

volume = pi * radius * radius * height
surfaceArea = 2 * pi * radius * radius + pi * 2 * radius * height

println "Volume of cylinder with radius ${radius} and height ${height} is ${volume}."
println "Surface area of cylinder with radius ${radius} and height ${height} is ${surfaceArea}"


The "happy path" output for this version of the script is the same as for the first version and will not be shown here. However, the usage information provided when an insufficient number of parameters is provided is shown in the next screen snapshot.



One problem still remaining with this script is that non-numeric values passed in for radius or height can be problematic as demonstrated in the next screen snapshot in which the second command-line argument (expected height) is a String.



One approach for dealing with this is to use the GDK-extended String method isNumber(). This is the approach applied in the next Groovy code listing.


// cylindrical3.groovy
//
// Calculates geometric characteristics of a cylinder with given radius and height.
//

if (args.length < 2 || !args[0].isNumber() || !args[1].isNumber())
{
println """You must enter at least two numeric parameters for radius and height.
Usage: cylindrical2 <<radius>> <<height>>"""
System.exit(args.length)
}

// Don't want to use Math.PI because want same accuracy as used in homework
pi = 3.14

radius = args[0].toBigDecimal()
height = args[1].toBigDecimal()

volume = pi * radius * radius * height
surfaceArea = 2 * pi * radius * radius + pi * 2 * radius * height

println "Volume of cylinder with radius ${radius} and height ${height} is ${volume}."
println "Surface area of cylinder with radius ${radius} and height ${height} is ${surfaceArea}"


The improved output now resulting from the providing of a non-numeric value for height is shown in the following screen snapshot.



Although this basic command-line argument checking is not difficult, it turns out that Groovy has more sophisticated command-line argument support built in. Groovy takes advantage of the Apache CLI library that I have previously blogged on and makes it even easier to use via CliBuilder. The next code listing demonstrates Groovy code using CliBuilder to handle command-line arguments. In addition, it provides several other examples of additional "grooviness."


#!/usr/bin/env groovy
// cylindrical4.groovy
//
// Calculates geometric characteristics of a cylinder with given radius and height.
//

def cli = new CliBuilder(usage: 'cylindrical -r radius_arg -l height_arg [-u arg]')
cli.with
{
h(longOpt: 'help', 'Usage Information')
r(longOpt: 'radius', required: true, type: Number, 'Radius of Cylinder', args: 1)
l(longOpt: 'length (height)', required: true, type: Number, 'Height of Cylinder', args: 1)
u(longOpt: 'units', 'Units of Supplied Radius and Height', args: 1)
p(longOpt: 'pi', 'Value of pi used in calculations', type: Number, args: 1)
}
def opt = cli.parse(args)
if (!opt || opt.h) return // parsing error occurred or usage was requested
if (!opt.r.isNumber()) {println "Radius must be numeric."; cli.usage(); return}
if (!opt.l.isNumber()) {println "Height must be numeric."; cli.usage(); return}

def radius = opt.r.toBigDecimal()
def height = opt.l.toBigDecimal()
def printableUnits = opt.u ?: "units" // "Elvis" operator
def pi = opt.p ? opt.p.toBigDecimal() : Math.PI

// Don't want to use Math.PI because want same accuracy as used in homework
//def pi = 3.14

def volume = pi * radius * radius * height
def surfaceArea = 2 * pi * radius * radius + pi * 2 * radius * height

println "All calculation are using ${pi} as the value of 'pi'"
println "Volume of cylinder with radius ${radius} ${printableUnits} and height ${height} ${printableUnits} is ${volume}."
println "Surface area of cylinder with radius ${radius} ${printableUnits} and height ${height} ${printableUnits} is ${surfaceArea}"


The use of Groovy's built-in support for command-line argument via Apache CLI and CliBuilder facilitates easy development of command line options with short (single hyphen) and long (double hyphen) names. Usage information is readily available via CliBuilder as well. In the code above, I used Groovy's with() to group the calls on my "cli" object.

Because CliBuilder is so easy to use, I threw in two more options to make the script output potentially more interesting and flexible. I also have support for radius and height to be more specifically specified so there is less change of switching the two numeric values on accident. The new command-line options allow the user to optionally specify the units of measurement and the value of PI used. If the units are not specified, the string "units" is used (thanks to the Elvis operator) and if the value of PI is not specified, the value from Math.PI is used by default (thanks to traditional Java's ternary operator).

The next screen snapshot demonstrates the flexibility of this final version of the script for calculating cylindrical volume and surface area.



As the screen snapshot above demonstrates, CliBuilder has allowed me to easily apply great flexibility and usefulness in the my script. Other online posts on Groovy and CliBuilder include Groovy CLIBuilder in Practice, Groovy Command Line Options, Groovy CliBuilder with Multiple Arguments, and Groovy Script: Get SSL Cipher and CLIBuilder Example.

Observations

Groovy Uses of Standard Java APIs and Third-Party Java APIs and Libraries

One of the compelling advantages of Groovy is readily usable Java libraries and APIs and Groovy's built-in use of Apache CLI is a great example of this. For example, if the built-in, Apache CLI-powered command line arguments handling is not exactly what you want, you could consider using another Java-based library such as Args4j. For a direct Groovy alternative to the built-in support using Apache CLI, consider Groovy Option Parser. Java's BigDecimal (which Groovy uses underneath the covers for floating point types by default) was also particularly useful in this example.

Groovy is Great for Scripting

Groovy brings the advantages of Java's rich feature set while at the same time allowing the scripts to be written more script-like.

Programmer Laziness is a Different Type of Laziness

As many blog posts have already pointed out, the "lazy" programmer is often not "lazy" in the traditional sense of the word. The "lazy" programmer tends to go out of his or her way to reduce the tedious tasks that he or she must perform. He or she may be willing to expend significant effort to generate a script to avoid tedium that might, especially in an isolated case, take less time than writing the script. However, if the situation is one that is repeated, the generated script should earn back its time spent many times over.

Conclusion

Groovy is the perfect fit for writing a simple script to perform floating-point arithmetic with precise desired precision. I actually used the first version of the script in my case because it was done in about five minutes, but it was fun to make it more elegant and present that here. Another observation from all of this might be developers' strange sense of "fun." Groovy gives me a scripting language with the language richness and features of Java.

Thứ Sáu, 20 tháng 11, 2009

Java 7: The Return of Closures

The Big Announcement: Closures in JDK 7!

The biggest current news in the Java world is the apparent reconsideration by Sun regarding the addition of closures to JDK 7. Mark Reinhold's Devoxx announcement has led to many posts on the subject including Stephen Colebourne's Closures in JDK 7, Alex Miller's Closures After All?, Ted Neward's Closures Are Back Again, Cay Horstmann's Closures? In Java 7???, Mitch Pronschinske's Closures Coming to Java 7, Geertjan Wielenga's Devoxx Day 3: Conference Day 1, and a summary of some of these posts in the Java.net editorial DEVOXX Surprise: Out of Nowhere: Closures in JDK 7!

The collective excitement, concern, confusion, and questioning attitudes represented by these posts is likely indicative of the general Java community's feelings regarding this announcement. I have blogged before regarding closures being (at that time we thought based on last year's Devoxx announcement) one of the most-missed features to not be included in Java 7. Dionysios G. Synodinos summarized, in Java 7 Roadmap Updated: Reactions, several online reactions (including mine) to the 2008 Devoxx announcement that closures were being omitted from JDK 7. The Java.net poll on most-missed Java 7 features showed closures being the most voted for "missed feature" with nearly half the votes. Even James Gosling has weighed in with his favoring of closures in Java. The JSR Proposal for Closures in Java also lists individuals and organizations particularly interested in Java closures. Closures are clearly popular with Java developers who read and write regularly in the blogosphere.


What Are Closures?

For those who are not familiar with closures, Martin Fowler's post Closure is perhaps the definitive explanation of what constitutes a closure. Fowler states about closures: "Essentially a closure is a block of code that can be passed as an argument to a function call." Fowler compares and contrasts closures with anonymous classes and highlights closures as syntactically simpler than anonymous classes and able to see variables available at time of closure definition.

Besides Fowler's Closure post, other useful introductions to closures can be found in the Groovy User Guide section on Closures (defines closures as "A Groovy Closure is like a 'code block' or a method pointer.") and in the Wikipedia entry on closures.


What Are the Java Closure Proposals?

The JavaWorld article Understanding the Closures Debate lays out the three most popular proposed alternatives for closures in Java. The three primary proposals for Java closures are BGGA, CICE, and FCM.

The BGGA proposal is named after the first letter in the last names of each of its original authors: Gilad Bracha, Neal Gafter, James Gosling, and Peter von der Ahe. It is often referred to as the "full closures" proposal and was by far the most popular of the three options voted on in this Java.net poll.

The FCM (First Class Methods) closure proposal is the one closest to the example syntax Mark Reinhold reportedly wrote on the airplane ride to Devoxx and it sounds like FCM 0.5 is considered the starting point for Java 7 treatment of closures. It currently sounds like the Java 7 implementation would exclude inclusion of the Java Control Abstraction (JCA) features.

A third Java closures proposal is the Concise Instance Creation Expressions (CICE) proposal by Josh Bloch, Doug Lea, and Bob Lee proposes "a more concise syntax to instantiate anonymous classes." Bob Lee's introduction of CICE closure proposal compares it to the BGGA proposal. Elliotte Rusty Harold has posted his complaints about the CICE proposal.

The blog post Comparing Inner Class/Closure Proposals compares the three above proposals and throws the Clear, Consistent, and Concise (C3S) for Java proposal into the comparison.


Additional Resources

Another good resource regarding Java and closures is Brian Goetz's The Closures Debate. The Closures Syntax in Java 7 Twiki page and Alex Miller's Java 7 page have numerous links to additional information on closures in Java.

Thứ Hai, 16 tháng 11, 2009

Groovy Console: Graphical Groovy Shell

I wrote about Groovy Shell in my last blog post. As I wrote in that post, Groovy Shell is a highly useful command line based method for experimenting with Groovy code. For those who prefer a graphical interface over the command line interface, Groovy Console provides a nice alternative and that is the subject of this blog post.

Assuming Groovy is appropriately installed on one's system with GROOVY_HOME defined correctly, it is easy to run the Groovy Console by simply typing groovyConsole at the command line. The next screen snapshot shows the running of Groovy Console along with the output from selecting Help->About.



It is easy to enter Groovy code and execute it (by clicking on the icon on the far right with the green arrow pointing right). The next two screen snapshots show entering Groovy code in the top pane and then the results of clicking on that "Execute Groovy Script" button in the bottom pane. Color coding helps make the scripts and the output more readable.





The Groovy Console supports larger fonts. The next screen image shows the results of selecting View -> Larger Font multiple times.



This image also shows a simple script for getting the date/time from Calendar using Groovy's property access syntax. Instead of needing to call getInstance() on Calendar and getTime() on the returned Calendar instance, I was able to access those directly with .instance.time.

Groovy Console allows one to select whether shortened or full stack traces should be displayed. The following two screen snapshots demonstrate the difference between the normal (shortened) stack traces and full stack traces. To turn on full stack traces, one selects View -> Show Full Stack Traces.





In this case, it is easy to deal with the stack trace. All that needs to be done is to import the Color class as shown in the next screen snapshot (classes in java.awt package are not automatically imported in Groovy).



Groovy Console supports numerous other useful features. Scripts can be saved to disk once they do what the developer wants. JAR files and directories can be added to the Groovy Console classpath for custom Java classes used by Groovy scripts. The Groovy Console page lists many more useful features of Groovy Console such as the ability to host Groovy Console as an applet and the ability to embed Groovy Console in Swing-based applications.

Another useful graphical approach to playing with Groovy code is the Groovy web console deployed on Google App Engine.

Groovy Shell: A Gentle Introduction to Groovy

Groovy Shell brings to Groovy what irb (Interactive Ruby) and jirb (Interactive JRuby) bring to Ruby and JRuby respectively. I have written about both IRB and JIRB and in this blog post I look more closely at Groovy Shell.

As IRB and JIRB do for Ruby and JRuby, Groovy Shell provides an easy, command line based interactive tool for experimenting with Groovy syntax. This can be easier to use for experimenting with Groovy than writing files to be run in a separate step or trying to pass multiple commands to the Groovy interpreter with the -e option.

Assuming that Groovy has been appropriately installed, it is easy to run Groovy Shell with the groovysh command. Doing so leads to the output shown in the next screen snapshot.



Entering "help" at the Groovy Shell prompt brings up usage information as demonstrated in the next screen snapshot.



At this point, I'll demonstrate via Groovy Shell capabilities that Groovy applies similar to capabilities I demonstrated previously for Ruby and JRuby using IRB and JIRB. The next screen snapshot demonstrates some Groovy math operations in Groovy Shell.



Groovy provides many String operations one might not expect in a language as tied to Java as Groovy is. However, the next screen snapshot proves some of Groovy's string operations that behave similarly to those demonstrated for Ruby and JRuby in my previous writings on IRB and JIRB.



The 2004 JavaOne conference presentation The Groovy Programming Language: Let's Get Groovy includes a nice tip related to use of Groovy Shell. In one of that presentation's slides, Rod Cope and James Strachan demonstrated using Groovy Shell to easily determine the methods available on a particular class. They specifically looked at the File class, but it could be used on any class as shown in the next screen snapshot with File and String.



There is much more that can be done with the Groovy Shell and more information is available in the online Groovy User Guide section Groovy Shell. Some problems previously encountered with Groovy Shell are covered in the Troubleshooting section.

For those who prefer a graphical tool similar to Groovy Shell, the Swing-based Groovy Console or Google App Engine powered browser-based Groovy Web Console is likely to be of great interest.

Thứ Năm, 5 tháng 11, 2009

RMOUG Training Days 2010 Agenda

An early version of the agenda for RMOUG Training Days 2010 is now available. The agenda currently shows six University Sessions, ten 90 minute sessions, ten 30 minute sessions, and then many of the typical 60 minute sessions. As usual, many of the presentations are focused on Oracle database development and administration. However, there are several presentations likely to appeal to those who have little or no interest in the database.

The following are some of the sessions that I think look interesting based on their titles and abstracts. There are more attractive sessions than I will list here.

There is only one session (Session 4, Wednesday, 17 February 2010, 2:45 pm - 3:15 pm) with 30 minute presentations, but there are many potentially interesting presentations during that session. These include Using SQL to Generate SQL (likely to be particularly useful for those new to SQL), Java 6 and Java 5 New Features (likely to be particularly useful for those new to J2SE 5 or Java SE 6), and 10 Things I Did Not Learn from SQL Class (very specific how-tos listed in this abstract).

Bill Jackson's 60-minute presentation 10 Easy Ways to Build a Better Graphic User Interface is scheduled for Session 7 (Thursday, 18 February 2010, 9 am - 10 am). My first presentation, RESTful Java is scheduled for Session 8 (Thursday, 18 February 2009, 10:15 am - 11:15 am) and Oracle Fusion Design Fundamentals is offered in that same session. My second presentation, Applied Groovy: Scripting for Java Development, is scheduled for Session 11 (Thursday, 18 February 2009, 2:45 pm - 3:45 pm).

There are multiple presentations on Service Oriented Architecture (SOA) including SOA Today during Session 9 (Thursday, 17 February 2009, 11:45 am - 12:45 pm). Other SOA presentations include Problems and Solutions Facing SOA Implementations and Using Agile Approaches and Techniques for Rapid SOA Implementations (both Session 11) and Agile SOA: Is It Possible? (Session 7).

Although Oracle is pursuing acquisition of Sun Microsystems, Java and Sun products play a relatively minor role at Training Days 2010. There are three presentations with "Java" in the title, which are my two presentations ("RESTful Java" and "Applied Groovy: Scripting for Java Development") and the above-cited John King presentation "Java 6 and Java 5 New Features." There are additional presentations that are Java-related such as presentations on Application Development Framework (ADF) and Fusion.

Several of the speakers scheduled for RMOUG Training Days 2010 are very well-known in the Oracle community. These include Robert Freeman, Steven Feuerstein, and Duncan Mills. There are also several speakers who frequently present at RMOUG Training Days as well as some speakers that are new to Training Days.

I always start to get more excited about attending a conference once its agenda has been posted. I am looking forward to presenting at and attending RMOUG Training Days 2010.

Thứ Hai, 2 tháng 11, 2009

Realizing That I Still Have Much to Learn

Several years ago, I was asked to rate myself on my level of expertise with different programming languages and frameworks. Several people were surprised when I did not rank myself at the highest possible level for knowledge of Java, especially when several developers with far less software development in general and far less Java experience in particular rated their own Java skills at the maximum level. I explained to them that I too would have at one time ranked myself at the highest possible level but that I had learned enough since that time to realize how much I still needed to learn. There was no way I could in good conscience rate myself at the highest level knowing how many areas of the Java ecosystem I lacked experience and depth of knowledge in. I believe there are numerous advantages to always understanding what one doesn't know (or working to discover what one doesn't know) and will outline some of these advantages in this post.


Ability to Consider New Options

When one thinks he or she already knows everything, that person has no incentive or interest in considering anything new or different than what he or she has done before. This can backfire because the same solution or approach rarely fits every problem a developer will encounter. It might seem easiest to shoehorn a particular problem into the solution we know best, but that does not make it the best or even an appropriate solution. By admitting that I may not know everything and have much still to learn, I will be more willing to listen to others' ideas and consider other perspectives. I still may end up going with my initial solution, but I will have thought out its benefits and costs and weighed them against the benefits and costs of the alternatives.


Ability to Learn New Things

If I think I know everything already, I have no need to waste time learning anything new. Learning new things can be tiring and even frustrating, but I am more apt to invest the time and effort in my own knowledge if I realize that there is so much still to learn.


Ability to Challenge My Own Assumptions and Biases

The person who knows everything (or at least thinks he or she is omniscient) has no reason wonder if what he or she is doing is correct for a given situation. The person who understands that he or she doesn't know everything is more willing to question his or her own actions and measure the results of those actions. If a course change is necessary, the person who understands his or her limitations is more likely to identify that need and do something about it. I have written before about the importance of confidence to a software developer. I think confidence is one of the most important traits of a successful software developer, but true confidence (as opposed to arrogance) does not preclude one from understanding that he or she is not necessarily always correct.


The Cost of Not Realizing There is Much More to Learn

I have seen many individuals who believe they have all the answers and make silly statements or, even worse, make unnecessarily costly decisions because of their arrogance and lack of understanding of their own limitations. By not having at least an idea of what one doesn't know, one is not able to sufficiently consider other ideas, to question the correctness of one's own decisions, and to realize the importance of learning new things.


The More We Learn, the More We Realize That We Don't Know

I have found that the more I learn and understand a particular subject, the more I realize how much more I still have to learn. It seems that I can only understand the bigger picture of a language or framework once I have begun to understand specific details of that language or framework. As I immerse myself in a new language or framework, I'm more likely to notice relevant articles posted on DZone or Reddit. Better yet, the more I learn about something, the more these blogs and articles make sense to me and the bigger the possibilities become.

As an example of this principle, consider a person learning the Java programming language. He or she may at first think only of the programming language itself and focus on keywords, syntax, and so forth. As the person gains familiarity with Java SE, he or she may start to be more exposed to Java EE principles. Similarly, the developer may start to learn about other languages on the JVM. In addition, that developer may start to learn about frameworks and libraries available for use. During all this, that developer continues to learn the Java APIs and how vast they are. What seemed like a simple language turns out to be a complex, ever-changing ecosystem.

The interesting phenomenon here is that because I have repeatedly realized how much more I still have to learn after I have learned substantially new information, I assume that I will continue to realize that I have even more to learn as I continue to learn and gain experience. In other words, the absolute amount of knowledge I would like to learn to be more effective is likely to only get larger, even as I learn new things. It's not that I realize that I need to learn more despite my current learning; rather it's precisely because I'm learning that I realize how much more I still need to learn.


Conclusion

When one understands that he or she may not be always correct, he or she is more likely to catch their own mistakes early before they are most costly, is more likely to listen to and seriously consider alternative approaches, and is more likely to keep bettering himself or herself. I am realizing that it is actually a good sign when I understand how much I still have to learn -- this often means that I'm starting to get a much better understanding of the big picture and how things fit together.