Why & How I Write Java By Steve Wedig
A few weeks ago I posted a software developer’s reading list. An important criterion for this list was technology independence because most software development skills transcend whatever tools we are currently using. That being said, our choice of tools is quite important. This article explains why I currently build software using Java (and its platform). I also describe how I write Java: techniques, conventions, and practices.
I’m no Java evangelist. I am frustrated by the gap between established languages and what I believe our profession is capable of designing. (One cause is the momentum of standardswhich makes them expensive to replace.) For me the goal of software development is to evolve valuable software with a team. I think Java, with its platform & ecosystem, is currently the best tool to support this multi-faceted goal. (This applies to general application programming, not to programming requiring explicit memory management.)
I recognize that using Java isn’t an interesting or exhilarating choice. I am going to be posting some open-source Java code, and I think it is useful and perhaps helpful to present the rationale for why I choose to use Java professionally. Also my perspective is informed and biased by my background, which is 7+ years of delivering web applications and services, starting with Python + JavaScript and then shifting towards Java. The right decision for my needs may not be the right one for yours.
Related Article: Teach yourself programming in 10 years
Why I Write Java
I use Java for application programming. I recommend Python or Ruby for learning programming. I don’t know what to use for programming requiring explicit memory management (i.e. C, C++, D, Rust).
Benefits of Java
- Platform Maturity: Java is stable and it’s about as fast as you’re going to get with garbage collection. It is depended on by huge companies. I want to spend time evolving valuable software, not fighting with semi-mature tools. (Examples of being forced to switch back to Java include Twitter and Yammer. Notice the dates on these, because issues do get ironed out. Here are comments about Scala’s current maturity: comment 1, comment 2.)
- Statically Typed: I’ve used Python professionally for 7+ years. It is a wonderful language that has taught me a lot. However I am convinced that static typing is a profoundly valuable tool for software development. Reasons include:
- Contextual Documentation: With static typing I always know the types for inputs and outputs. I know exactly what I have to provide as input, and I know the attributes available on the output. This makes it a little easier to work with code I remember, considerably easier to work with code I can’t remember, and a ton easier to work with code I’ve never seen before (i.e. learning the AWS SDK for the first time).
- Explicit Interfaces: Object-Oriented Component Design is about managing interfaces. With explicit interfaces I can more easily focus on interface design without worrying about the implementation details (classes). When I do get to implementing a class, I can declare what interfaces it is supposed to implement, as well as the interfaces of the dependencies. These declarations are great documentation and are used by tool support to help me.
- Tool Support in an IDE: Tool support makes programming easier so I can focus more on higher level issues. Examples of how it helps include:
- When an interface changes, the out-of-sync clients and implementations go red.
- I have confidence that the automated rename refactoring works. If it doesn’t work, I expect something to go red.
- If I want to implement an interface, I can auto generate a class with all the stub methods. Then I can write fields on the class and auto generate a matching constructor. (This last part sounds possible with dynamic typing + conventions.)
- I can call a non-existing method on an object and the call goes red. Then I auto generate a corresponding method on that object’s interface. Then that object’s class goes red because it doesn’t implement that method yet. Then I auto generate a corresponding method stub on the class.
- Prevent Type Errors: With static typing I discover type errors instantly instead of finding them when running tests, or even later if I don’t have perfect test coverage. I find rapid prototyping to be a easier when I have static types to lean on. When I was switching from Python to Java, I found I could throw together working code faster with Java, despite Java’s annoying verbosity and despite knowing Python much better.
- Write Once, Run Most Places: With Java I have one language that can target servers,Android, and browsers via Google Web Toolkit (GWT). (It unfortunately doesn’t help with iOS or programming with explicit memory management.) I believe that fragmenting a team’s codebase into multiple languages introduces a tremendous amount ofaccidental complexity and redundancy. (Commenters have suggests J2ObjC andRoboVM to reach iOS from Java.)
- Libraries: Java has libraries for what you need, and those libraries are often pretty good. In my experience I have found Java libraries to be more solid on average than in Python or JavaScript. Often Java will have reference/official clients for stuff like AWS, AMQP, Thrift, etc.
- Dependency Packaging: I really like being able to package all the dependencies into aJAR or WAR file for deployment. Other languages obviously have solutions to this, but I’ve found Java’s solutions to be at least easier than Python’s virtualenv.
For an alternate perspective, Michael O. Church wrote a nice 2010 essay about the benefits of static typing. In 2013 he wrote an article about why he has softened his static typing stance and has found Clojure to be great.
Problems with Java
The cons are mostly language design issues:
- Insufficient Type Inference: This is inane. Java 7′s diamond operator helps some, andGuava provided some of its benefits for Java 6.
- Only One Superclass/Mixin/Trait: Implementation inheritance causes problems. Multiple implementation inheritance causes even more. I prefer to instead use composition when possible. However without syntactic support to delegate to nested objects, you do need multiple mixins sometimes.
- Verbose Lambda Syntax: Java 8′s lambda should fix this.
- No Get/Set Properties: I want to be able to use “x.y” and “z = x.y” while preservinguniform access. This is available in at least C#, Python, and Ruby.
- XML Configuration: I prefer to use code files for configuration. If it must be a separate language, then YAML would be better. Of course XML isn’t inherently part of Java, but it is used by the community in practice. (For example, I have to wrestle with Maven XML files.) (These comments say the community is trending towards less XML: comment 1,comment 2, comment 3.)
- C Style Syntax: I prefer significant whitespace, like Python. This is just a personal preference.
- CamelCase: I prefer snake_case, because _ reads more like a space, which is how we usually separate words. This is just a personal preference. (I think the best choice would be to do auto conversion between snake case and camel case based upon the programmer’s preferences and needs. It could then be an IDE setting for how to render the code. You’d have to force CamelCase and snake_case to be mutually exclusive, so this is probably only possible for a new language.)
- … I’m sure this list could be arbitrarily long, but you get the idea …
Why I Don’t Use <other language>
(I include these notes in order to fully explain my thought process, as I would to a friend. Please don’t beat me up over it.)
Here are a few reasons why I currently choose Java over other languages (in addition to the fact that many of them can’t target browsers):
- C#: I don’t use C# because I’m not interested in Microsoft’s ecosystem (i.e. VisualStudio, Azure). But if you are, C# seems like a good alternative to Java. C# was originally Microsoft’s answer to Java. In some areas it has more gained more advanced features (comparison of features). I particularly like LINQ. I think it’s cool that they hired Haskell folks like Eric Meijer and SPJ.
- Scala: Scala is probably the language with the closest design to what I’d want. Building on the JVM puts it on the “fast” track to maturity, but I don’t feel like it is there yet. Here is a dialogue about issues that might arise in practice. (Comments about Scala’s current maturity: comment 1, comment 2.)
- Clojure: Clojure is innovative, but it is dynamically typed and presumably not mature enough yet (although building on JVM helps again). Clojure’s explanation of state and mutation is great. The language seems particularly innovative in the area of modeling shared memory. Rich Hickey has given some great presentations.ClojureScript is of course interesting to me. In my heart I feel that Lisp syntax hinders readability and macros are too fancy, but having read amazing Lisp books, I wonder if I’m a bit ignorant to believe that.
- Python: Dynamically typed. Platform and libraries are less mature than Java.
- Ruby: Dynamically typed. Platform and libraries are less mature than Java.
- Perl: Dynamically typed and worse design than Python and Ruby. (Of course worse than the best does not mean bad. Like many people, Perl was my first scripting language, and it really opened my eyes. I would be worse at writing Java if I hadn’t experienced Perl and Python.)
- PHP: Dynamically typed and worse design than Python and Ruby. (I think software is valuable if it is used, and by this metric PHP has been very valuable. It isn’t the best for my needs though.)
- JavaScript: Dynamically typed and worse design than Python and Ruby. (Brendan Eich’sinterview in Coders at Work provides great context. Given the constraints, I am thankful that he did such a good job.) Regardless, it runs in the browser. If I had to write JavaScript instead of compiling to it, I would consider using a pre-processor likeCoffeeScript. There is a certain logic to attempting server side JavaScript, but this makes me sad.
- Dart: Not mature enough. Like TypeScript its goals and corporate backing make it interesting to watch.
- TypeScript: Not mature enough. Like Dart its goals and corporate backing make it interesting to watch.
- Go: I’ve only looked at the docs. The ecosystem maturity is my primary concern, but like Dart and TypeScript, it is interesting to watch.
- Haskell: Insufficient adoption and difficult to work with. Real World Haskell is worth reading, but I don’t find Haskell code particularly readable. Haskell does not appeal to me as a software developer who loathes “tricky” code. (These are just my impressions, don’t take my word for it.) That being said, Haskell is motivated by numerous unassailable insights and benefits. Languages should make immutable the default and should strongly encourage (but not require) writing wide swaths of referentially transparent pure code. You can and should blend FP with your OO design.
- F#: Other than the idea (FP on .NET), I don’t know much about it in practice. Here is someone’s comment.
- Groovy: Other than the idea (scripting on JVM), I don’t know much about it in practice.
- Objective-C: Haven’t looked into it much, but I will when I write software for iOS.
- Other Languages: There are many languages with communities that are too small for me to be comfortable using them on professional projects: OCaml, ML, Common Lisp, Scheme, Smalltalk, etc. Obviously not everyone shares this opinion. Also, how will a community get bigger unless someone is willing to be the tip of the spear?
- Programming with explicit memory management: This bucket includes C, C++, Rust, D, etc. I haven’t needed to explicitly manage memory, so I haven’t used these professionally. (Other than C, C++ and assembly as a student and a teaching assistant.)
How I Write Java
One of the concepts on my software developer’s reading list is Code Design: Writing intention revealing code. My starting point for how to write clean Java is two excellent books:Clean Code and Effective Java (2nd edition).
In addition to the material in these books, here are other practices I follow when writing Java…
Dependency Inversion & Type Naming Conventions
Dependency Inversion means having your dependencies provided to you, rather than statically importing them or building them yourself. This increases modularity and isimportant for testing. Ideally you statically import (depend on) interfaces, not classes orsingletons. I think this is why the first suggestion in Effective Java is to use static factory methods. Here is an example of the organization and naming I use to follow this advice:
- Employee (public interface): The employee interface. I prefer this over theIEmployee convention.
- EmployeeClass (package visibility class): The employee class. (The implementation.) I prefer this over the EmployeeImpl convention.
- EmployeeLib (public abstract class): The library for creating instances of Employee. I prefer this over the plural Employees convention, but not by much. This library contains the static factory methods, as well as other related miscellaneous functionality. If there are multiple factory methods, only one uses “new”, and the others delegate to it. I prefer having a separate file for the EmployeeClass rather than nesting it inside the EmployeeLib.
- PersonMixin (public abstract class): A shared superclass. Even though I prefer composition over inheritance, implementation inheritance is useful for quick behavior reuse or implementing the Template Method Pattern.
If you strictly follow these conventions you will never have the words “public class” in your codebase (be pragmatic though). Notice that if a CompanyClass statically imports EmployeeLib to create an Employee instance, then that isn’t quite dependency inverted yet. This instance isn’t provided, it is retrieved. However it would be easy to invert this dependency by instead passing an Employee instance or Employee factory into the CompanyClass constructor (or using some other form of Dependency Injection).
Here’s a related Google Presentation: Don’t Look for Things.
Other Practices I Follow When Writing Java
- Use Google Guava as a Java extension (philosophy, compared to Apache commons).
- Use jUnit for unit tests, but TestNG also seems popular (list of frameworks).
- Use Mockito for mocking (but I prefer to use fakes when possible).
- Use Maven for building. (I find it difficult to use at times but it is the standard.)
- Use Eclipse as an IDE, with the Google plugin for GWT. There is also an AWS plugin. IntelliJ IDEA appears to be a well liked alternative to Eclipse.
- Use Google’s Eclipse Java Formatter to follow Google’s Style Guide.
- Have Eclipse require @Override annotations.
- Use Guava’s Optional to avoid using null (Sir Tony Hoare’s billion dollar mistake).
- Make objects immutable by default. Guava’s immutable collections help. If an immutable object hashes based on state instead of memory address, then it is called a value object. Among other benefits, immutable objects remove an entire category of bugs and complexity.
- Despite believing everything should be immutable by default, I don’t use “final” everywhere. I think it clutters the code. I wish “final” was the default so you’d have to mark variables as “mutable”.
- Try to write referentially transparent functions. These functions don’t have side effects and their output is simply a function of input, not when the function was called. In Haskell this kind of pure code is the default. In Java it is not the default, but it should be our default in practice. Among other benefits, referentially transparent code removes an entire category of bugs and complexity.
- Prefer composition over implementation inheritance. I think this is a general consensus.
- Keep things small: methods, classes, and directories.
- Keep the numerous component design principles in mind.
- Avoid accidental ordering by preferring Set and Map over List. (I suppose this can affect performance, but most code isn’t a bottleneck.)
- Pragmatically adapt to context instead of dogmatically adhering to context-free rules such as these.
转自http://stevewedig.com/2014/02/17/why-and-how-i-write-java/


浙公网安备 33010602011771号