17 ms·
Better Java – Resources for Writing Modern Java
- izzle49 11y agoPlease don't use Java 8's default methods in interface's to do multiple inheritance, and code reuse. Prefering this over 'Util' classes is bad as it is inheritance over composition. The default methods were designed to facilitate easier interface migration
- gutnor 11y agoThat reminds me of "constant interface" anti-pattern - abusing a feature because it brings a small technical benefit. If you are unhappy with candy-shop utils class and are ready to split them into smaller entities, go the whole way there. Create proper small classes, and inject them using your chose DI technology. Anyway, kudos to the author, that was a risky article to put together and there was no way to write it without controversial recommendations. That's a good start for what a new java developer need to look for.
- lmm 11y agoJava was designed for TV set-top boxes, but that doesn't mean it's bad to use it on a server. If using a default method in an unintended way leads to clearer, more maintainable code, do it.
- izzle49 11y agohow would it lead to more clearer code? You would have many interfaces referencing an instance variable, each with different semantics for that variable. Interfaces cant have variables so they should not be used as traits.
- lmm 11y agoOne good use case I can see is where you want to add convenience "helper" methods to an interface that can reasonably have default implementations in terms of some other method on the interface (E.g. overloads to emulate default parameters). Or for methods that can be implemented in terms of another method, but might have a more performant direct implementation (e.g. Collection#get can be defined in terms of iteration). I don't quite understand your point about instance variables. Instance variables are and should be encapsulated. An object that implements multiple interfaces must of course provide the correct semantics for all of them. But that's true whether an interface contains method implementations or not.
- GhotiFish 11y agoInheritance over composition. I know what these words mean, but I don't see your meaning. Also, I don't think that's what default methods are for. The new StreamUtil libraries uses default interfaces. It's not migrating anyone, but the default methods describe intrinsic properties about the classes that inherit the interface. A predicate can be in sequence of another predicate, for example.
- zatkin 11y agoStreams are questionable in performance vs non-stream approaches. I haven't found an article that details performance differences, but I will say that I personally tried doing things using streams and doing the same things using non-streams and have had mixed results, one being faster than the other in some cases.
- mabbo 11y agoThe benefits of streams isn't performance improvements for the CPU, but performance improvements for the developer.
- chii 11y agoi have colleges that claim streams don't necesserily make the code more readable. Imagine a triple nested for-loop - making it a triple nested streams (e.g., where your map method is yet another stream), doesn't seem to really make it any more readable. The only reason to use streams, i think is the possibility to automatically parrelize, and secondarily to make it more readable.
- mabbo 11y agoI would take any triply-nested for loops, and reduce each loop to its own method. Inside that method would be nice, easy-to-understand stream logic rather than for-loops. The Java compiler will unroll the methods anyways, so you get easy readability without any loss of performance.
- kodablah 11y ago"If you're using Java 8, you can use the excellent new Optional type. If a value may or may not be present, wrap it in an Optional class like this" While I agree with most things, overuse of Optional like this is an antipattern IMO. Having done a lot of Java 8 development, I find Optional is best for return types, but otherwise forcing callers to wrap values in Optional is unnecessary as opposed to something like Scala. Those that work on the JDK feel the same [1][2] and I think [2] is even overuse with the prevalence of static analysis tools and @Nullable. Although a tad dated, I find [3] to be a good guide to modern Java all around. 1 - http://stackoverflow.com/questions/26327957/should-java-8-getters-return-optional-type/26328555#26328555 http://stackoverflow.com/questions/26327957/should-java-8-ge... 2 - http://blog.joda.org/2015/08/java-se-8-optional-pragmatic-approach.html http://blog.joda.org/2015/08/java-se-8-optional-pragmatic-ap... 3 - http://blog.paralleluniverse.co/2014/05/01/modern-java/ http://blog.paralleluniverse.co/2014/05/01/modern-java/
- yrro 11y agoI'm curious, which aspects of An Opinionated Guide to Modern Java have dated?
- kilink 11y agoI'm also not a fan of passing Optionals into methods. Most of the time it's easier and cleaner just to write an additional method without that optional parameter.
- Peaker 11y agoIt can grow cartesian though. I think the best compromise is to declare a new data type for the argument, such as: data ExecutionDirectory = ExecuteInCWD | ExecuteInDir FilePath And take an ExecutionDirectory instead of a `Maybe FilePath`. Of course, you need light-weight data declarations for that.
- harryh 11y agoKeep in mind that Tony Hoare, the inventor of the null pointer, calls this invention his billion dollar mistake. I generally think that he is right and use of null should almost always be avoided.
- sigzero 11y agoGuava is on github now. Might want to change the link.
- zinxq 11y agoI'm in agreement with the first few comments already - at least some parts of this advice is dubious. For one - some performance examples (specifically, integer array iteration) is 15 times slower with streams. Angelika Langer's analysis: https://jaxenter.com/java-performance-tutorial-how-fast-are-the-java-8-streams-118830.html https://jaxenter.com/java-performance-tutorial-how-fast-are-...
- coldtea 11y ago>For one - some performance examples (specifically, integer array iteration) is 15 times slower with streams. That's a very misleading example, and even the linked post itself explains why it is so (e.g. not much is done with the integers in the first place).
- speakeron 11y agoI would question these results. I ran the same code on a more modern processor (i7-3930K) for 1000000000 iterations and got these averages over 10 loops after a warm-up: Array: 453 ms Stream: 887 ms Parallel stream: 120 ms The array loop code is about twice as fast as the sequential stream (parallel stream blows both of them away). The array loop advantage will start to go away once you increase the complexities of what you're doing in the loop and, of course, the stream code is much more readable. (Edit: I also tested IntStream().of() instead Array().stream() for the sequential stream test, the results were identical in both cases.)
- Randgalt 11y agoNice compendium. I don't agree with everything, but I doubt you can get an Java devs to agree on everything.
- fizwhiz 11y ago>> Good alternatives to using Spring is Google and Square's Dagger library or Google's Guice. They don't use Spring's XML configuration file format, and instead they put the injection logic in annotations and in code Spring has had code-based configurations since 2009[1]; seems a little disingenuous to recommend Guice/Dagger due to this non-existent limitation. [1] http://spring.io/blog/2009/12/22/configuration-simplifications-in-spring-3-0/ http://spring.io/blog/2009/12/22/configuration-simplificatio...
- 6502nerdface 11y agoNot sure how you get that reading... the author makes no claim that Spring is limited to XML configuration. In fact, in the paragraph just before the one you quote: > It [the Spring framework] has a either code-based wiring or XML configuration-based wiring.
- fizwhiz 11y agoI read the paragraphs a little like "Spring has code-based config and xml-based config (careful with the xml-based config!) Guice and Dagger don't have xml-based config, so use Guice or Dagger". My gripe is with the hand-wavy treatment of why Guice/Dagger are better alternatives just because they don't have xml-based configs even though the author knows that people can use Spring exclusively without xml-based configs (this is why i said it was disingenuous)
- lmm 11y agoSpring has a million and one ways to do anything; that's the problem with it. Spring with a very strict, carefully enforced style guide is probably as good as Dagger or Guice. But there is a cost to maintaining and enforcing a style guide. Better to just use something that doesn't have the bad options.
- jacques_chester 11y agoThat's what Spring Boot is for. Everything is autoconfigured and set to sensible defaults. All the dependencies in the starters are levelled and guaranteed to work together. Spring Boot on Gradle is the closest thing Java has to adding lines to a Gemfile in a Ruby system. Especially when you add other Spring automagical libraries (Spring Data REST, Spring Cloud...).
- ryanseys 11y agoThe only way it seems you can be effective with Java is with an IDE to do most of the heavy lifting. What would be better is if Java shipped with more focused command-line tools and better defaults that allow me to productive immediately without the need to learn something as hefty as IntelliJ / Eclipse. This could lessen the barrier of entry to Java and widen community adoption.
- yogthos 11y agoOr you could just use modern JVM languages like Clojure.
- codarama 11y agoLisp? Lisp syntax makes less sense than lambda and stream which have ruined Java.
- EdwardDiego 11y agoI like Clojure, but anyway to make it start faster in a scripting context? I wrote the same script in Clojure and Kotlin, and the Kotlin one loads so much faster.
- yogthos 11y agoThere's Planck for OS X https://github.com/mfikes/planck https://github.com/mfikes/planck It uses JavaScriptCore and starts practically instantly. There's some work being done to port it to Linux/Windows as well. Here's an example: cat foo.cljs #!/usr/local/bin/planck (println "hello world: clojurescript -> planck -> as shell script") ./foo.cljs hello world: clojurescript -> planck -> as shell script Planck can be installed using homebrew now as well. Another promising option is Pixie https://github.com/pixie-lang/pixie https://github.com/pixie-lang/pixie
- EdwardDiego 11y agoCheers mate :)
- tootie 11y agoI've been using spring loaded instead of jrebel and it works pretty well and is free. One thing I'm surprised he didn't mention is exception handling. Namely the growing acceptance that checked exceptions are dumb. I make a point that except for a few rare circumstanes, my code should never have a throws clause. I always created subclasses of RuntimeException (or Spring's NestedRuntimeException) and use them exclusively. Any libs that throw checked exceptions get either handled or rethrown with a custom RuntimeException.
- saveliyb 11y agoNice! Seeing "enterprise" java code anyways makes my blood boil, and I keep reminding myself that it doesn't have to be that ugly.
- incepted 11y agoIf you really want to describe modern Java, you should probably mention RxJava and TestNG.
- fizwhiz 11y agoTestNG may have been considered a "modern" alternative years ago, but today jUnit4 has caught up with most (if not all) of testNG's features.
- Cthulhu_ 11y agoRe the first bit about 'struct' style, even better (IMO) is to use Lombok; throw an @Data annotation on your class and all you need to do is put your properties in there (private final if need be). Nobody should be writing those boilerplate constructors, getters and setters. If it's generated by your IDE, use Lombok. The best code is no code.
- chii 11y agothen you suddenly force anyone who uses your code to depend on lombok. I tried to find a compile time only mechanism for lombok - not sure i found it, but it ought to exist!
- radq 11y agoLombok does rewrite bytecode at compile time only. Your dependencies will not have to depend on Lombok.
- monksy 11y agoThats one of the things that'll happen to you if you start writting groovy code. The whole setter/getter boilerplate thing gets really annoying. Another groovy-ism.. the whole deal with having to copy properties. (In a groovy class they're threated like a map)
- vorg 11y ago> properties. (In a groovy class they're threated like a map Except in hard to remember special cases like .class which gives the class "LinkedHapMap" of the map object instead of the value at key "class". After working with Groovy for a short while you'll realize you're really working around it.
- Babylonian 11y agoLombok is a crime. Have you seen what happens when someone does a minor reformatting of your code and moves one field declaration above another? You get a nasty little surprise because the two fields of the same type have now reordered in your Lombok generated constructor. When the app runs nobody can figure out why foo is set to bar and bar is set to foo. There's also the issue of Lombok vals which don't conform to the language spec leading to other fun. And it's almost always incompatible with new jdks without code changes to the lombok libs due to the whacky way it relies on implementation specific compiler details.
- hiram112 11y agoI've seen numerous posts lamenting the use of typical Java Beans. The problem with the struct approach is that numerous tooling / libraries expects beans with typical getter/setters - Jackson Json, Spring binding, etc. Yes it's possible with configurations and/or annotations to get around this, but IMO, saving a few lines of code in exchange for non-default behavior isn't a valid trade-off. Adding a dependency on Lombock is even worse. Honestly, I can spot a Bean class within 1 second of opening it up. Unlike Scala or other languages, Java programmers typically use one class per file, and beans rarely get in the way as they're typically contained in some 'model' package anyway.
- vbezhenar 11y agoAnother problem with struct approach is inconsistency. Sometimes you want a computed property and you use non-trivial getter. Sometimes you want to do some work in setter. And then client's code will look terrible: point.x * point.x + point.getY() * point.getY(). There are other problems: binary incompatibility; non-trivial refactoring when I need to introduce a getter or setter. Java really need lightweight property syntax which would solve all those problems. I don't understand why it isn't introduced yet.
- logn 11y ago> Sometimes you want a computed property and you use non-trivial getter Then pre-compute it, make it immutable, and expose the variable. And if that ends up being too hard it's probably because you need a builder and/or to group your properties into smaller classes.
- barrkel 11y agoPrecomputing properties on a hundred million object instances is not cheap.
- jimueller 11y agoGoing from C# to Java, one of the biggest things I miss is the Property syntax.
- trbvm2 11y agoDependency Injection (frameworks) -1 Use constructors. This makes perfectly testable classes and is vastly simpler than encouraging more XML as code. Any proper dependency injection framework should work quite well with regular constructors.
- fizwhiz 11y agoYour comment is unclear. Are you encouraging people to NOT use DI frameworks, or using them only with constructor-based injection?
- trbvm2 11y agoThe latter. DI frameworks are a convenience item, they should probably not have a big effect on the way the code is written. You get most of the benefits of DI just from not new'ing up things within your classes. The main advantage of keeping the constructors around is that you retain more flexibility in interacting with that bit of code.
- vbezhenar 11y agoConstructors have their problems. First: constructor calling is not readable because Java misses named parameters. It's fine when there are 2-3 dependencies, but not more. Second: circular dependency is not possible. So I prefer to use setter dependencies, as they don't have those problems.
- kilink 11y ago> First: constructor calling is not readable because Java misses named parameters. It's fine when there are 2-3 dependencies, but not more. If you have more than 2-3 dependencies, it might be time to refactor things, as your class may be doing too many things. > Second: circular dependency is not possible. Circular dependencies are possible with constructor injection when using a DI framework (e.g., using a Provider in Guice [1], or the equivalent in Dagger). [1] https://github.com/google/guice/wiki/CyclicDependencies https://github.com/google/guice/wiki/CyclicDependencies
- 11y ago
- kilink 11y agoI'm surprised he didn't mention AutoValue [1] or Lombok (using @Data and @Builder) in his section about data objects and builders. I prefer AutoValue, but both approaches cut down on a lot of the boilerplate (and they also generate sensible equals(), hashCode(), toString(), etc). It's also kind of a nitpick, but I disagree with his preference for Guava's Maps.newHashMap() vs new HashMap<>(). I'm pretty sure the only reason those static methods exist is because pre-Java 7 didn't have the diamond operator. [1] https://github.com/google/auto/tree/master/value https://github.com/google/auto/tree/master/value
- adpreese 11y agoHe mentions Lombok in the tools section. And Guava's collection creation methods are part of an intense desire to not use the new key word anywhere in application code.
- kilink 11y agoI realize he mentions Lombok, but he specifically bemoans the extra boilerplate with writing builders and data classes, but does not mention the @Data or @Builder annotations...
- kilink 11y agoAlso, if you look at the Javadocs for some of the creation methods, they explicitly state that they are for Java 6 and earlier, and that modern code should just use the constructor with the diamond syntax. http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Lists.html#newArrayList() http://docs.guava-libraries.googlecode.com/git/javadoc/com/g...
- Pyxl101 11y agoThe other replies kind of hit on this, but I'll expand on it by saying: The collection creation methods existed because Java could perform type inference on methods but could not on constructors: // Java 1.5 Map<String,Integer> map = new HashMap<String,Integer>(); // Guava Map<String,Integer> map = Maps.newHashMap(); // Java 1.8 diamond notation Map<String,Integer> map = new HashMap<>();
- carsongross 11y agoThere is some good stuff in here, and a lot of bad. SparkJava (mentioned) is the great hope for sane java web development: http://sparkjava.com/ http://sparkjava.com/ Having to convert out to streams for data structure manipulation with lambdas is insane. It would have been very healthy for the core java team to pull in someone with a lot of ruby experience so they could get that API right. Too bad: the JVM and associated tools are fantastic.
- asdf1234 11y ago> Having to convert out to streams for data structure manipulation with lambdas is insane I don't see why. It's explicit and allows you to chain higher order methods without building up a ton of intermediate data structures and convert to any data structure you like very efficiently. It's still not efficient as imperative approaches but it's close enough and far better than most collection libraries.
- potatosareok 11y agoHave you seen Goldman Sachs collection library? I've been using it for my Java work for last year and found it to work well. Put's a lot of useful methods onto the Collection objects themselves.
- joesmo 11y ago'Formatting is so much less important than most programmers make it out to be. Does consistency show that you care about your craft and does it help others read? Absolutely. But let's not waste a day adding spaces to if blocks so that it "matches".' I do agree that it's less important, but it's so easy to do that there is no excuse for not doing it or giving out such rubbish advice. Anyone that wastes a day on formatting is doing it wrong. The problem with advice like this is that people take it to the extreme and ignore formatting altogether. That leads to someone being frustrated and formatting all the files automatically (as should be done anyway) which then leads to possible conflict and crappy diffs. Instead, you could have had professional programmers work on these. In an industry that's touting how code readability is so important (and it is), this advice is toxic. We're finally getting people to realize that readability is the most important part for most code because it is. Honestly, formatting is important in this inconsequential reply, in essays, in everything we write except short chat messages. Imsurenoonewouldliketoreadsentenceslikethis just like no one wants to read obfuscated, unformatted, crappy code. So yeah, I disagree strongly because formatting is important, get one style, get proper tools to automate it, and then you don't have to spend a day putting in spaces (which is ridiculous).
- nobleach 11y agoAfter working at two companies filled with Java devs, I don't think any of them will read this article. I can't speak for all Java devs obviously, but it seems unless the dev is a polyglot, they are just fine living in their Spring/SVN/J2EE/Java5 world. They have no idea what's going on in the software development world. I get so excited whenever I hear any of them mention Clojure or even Scala! They just live with this head in the sand mentality. They hate the front end, they think, "these JavaScript developers will go away someday and we can finally go back to writing JSPs". Currently we've updated our JVMs to 8, and that's a big deal, but we've told our devs NOT to use any features from 8 as we're worried about how GC will perform and since we're about to get geared up into shopping season, now is not the time to use shiny new features.
- agumonkey 11y agoMentioning Scala, or clojure or even Haskell to a HR interview leads to raised eyebrows at best. But that was expected. I'm surprised actual devs are still wearing eye blinders.
- nobleach 11y agoI'm hiring front end JavaScript developers right now. If I see other language experience on their resume, they're getting a call. My last hire had worked through "Learn You A Haskell" and that was enough for my interest to be piqued. Seeing Python, Ruby, C, ObjectiveC, heck even PHP is enough for me to see they're not just a "I did some Angular tutorials" type of candidate. I'm on this site, so I get a nice smattering of different technologies. I don't write Rust, but I know it exists. I've read about it. Same goes for a ton of other technologies/languages/methodologies that I don't deal with. It just floors me that anyone in the development/engineering world doesn't do the same! They must believe their language/tech will exist forever.
- agumonkey 11y agoI should have added that it was IT consulting shops HR I was referring to, not final clients. The further away from the actual jobs, the more buzzwordy it gets. Some might appreciate curiosity and diversity, but in the end they want simple and bankable. They told me, some missions will enjoy jack of all trades type, but the majority of their clients wants framework-specific forces.
- sandGorgon 11y agoWhat web frameworks are you guys using in the Java 8 world that lets you leverage newer language features? Is it still Spring ? Are you guys doing things like Socket.io , etc. And is Hibernate the preferred orm ? Confession: I'm an ex Java programmer, who have been working in rails and python for past several years...so am fairly out of date. I have kept up with things like Dropwizard...but have not seen many other startups do Java development.
- jacques_chester 11y agoSpring is on Java 7 for the 4.x series. Spring 5 will take Java 8 as the baseline.
- namelezz 11y agoI note that it's not safe to pass Java 8 streams around as streams can be closed when invoking terminal operations. If you pass a stream to a method and do not know whether the method is going to close the stream, it's dangerous to reuse it after the method return.
- EdwardDiego 11y agoWell, once a Stream has been consumed, you can't use it again. We have a rule of thumb in my team that our APIs should not consume or return Stream or Optional.
- tigeba 11y agoI agreed with most of these suggestions. I'm not sure the suggestion to use a struct pattern is worth the lack of JavaBeans support in argle support libraries. You can always use final properties and only provide getters. I think asserting that Fluent interfaces are 'more readable' or better is largely a matter of opinion.
- eddiezane 11y agoThis is a really great resource and I'll certainly be embracing some of it. I attend quite a few university hackathons as a mentor/sponsor and one of my goals has been to paint Java in a better light as it doesn't have the best reputation in that circuit. I'm always telling people that their goal should be to build something cool and learn something new. It's much easier to teach someone how to build their first webapp in a weekend when you can do it in a language that they are familiar with and Java seems to be what's taught at most universities. The Spark Framework mentioned here is very well done and beginner friendly. I recently wrote an intro blog post [0] about it as an attempt to embrace existing knowledge and showcase that it's easy to build something cool with Java. 0: https://www.twilio.com/blog/2015/09/getting-started-with-gradle-and-the-spark-framework-3.html https://www.twilio.com/blog/2015/09/getting-started-with-gra...
- potatosareok 11y agoLooking at Spark framework I'm wondering about your thought of Spark vs Spring Boot. I've not used Spark but I find Spring Boot lets you get an app of the ground quickly, is part of massive Spring ecosystem which gives you a lot of other advantages (for example, Mongo template might be very useful for Hackathon project). Beyond that RestController automatic integration with Jackson can quickly let you bring up a JSON based webservice. Also I'm not a fan of how in your example the entire app routing/logic is basically crammed into a single main method. I know it might be simple demo app that you're trying to show off Twilo but I don't think you're doing favor by structuring like that. What do you feel are the benefits of Spark vs Spring Boot (or Dropwizard I guess). In my mind the only reason I wouldn't use Spring-Boot for a new project in Java is if I wanted to use something like Play! since supposedly using Play!/Scala and futures integrate very nicely into that.
- bluejekyll 11y agoAs a Java Dev of 15 years, through my own experience, almost all of these points I have discovered through hard fought bugs and trying to find a way to reduce the causes of them in the future. Final by default is the biggest, bc it removes some of the most basic concurrency errors implicitly. Awesome post. It also is pointing out things that in some other languages (e.g. Python) are implicitly impossible to achieve, and others which some new languages (e.g. Rust) have built in from the beginning, like never use null. Well done.
- mabbo 11y ago95% agree with these opinions, but have a few disagreements, or perhaps the author had missed some great stuff. Two main ones: Mockito is, IMHO, much nicer than jMock. And Immutability is good, but 'final' is... complicated. I find it's often over used by people who don't know why they're using it.
- meddlepal 11y agoCan you expand on the later point about final please?
- mabbo 11y agotl;dr: final parameters != immutable and safety, and they look like they ought to. A lot of people write "final" in front of every. single. declaration. And they do that because someone once told them that was good. But then you remember that Java is a language which all non-primitive types are passed by reference, so your 'final' is actually a final reference and not a const immutable object, like you might get in C. If someone passes in a final FooBar to a method, and you mutate the FooBar during the method for any reason, then after the method is done the object remains mutated. Just this week, that exact problem caused my team an entire day of headaches. Two methods were being called concurrently using Futures, passing the same object to each method. One method mutated the object slightly, the other relied on it having not been mutated. Hello race condition. I was so proud to have solved it, until I realized I'm the dolt who wrote the bug in the first place. Silly, I know. But the point is that final makes you feel safe when you might not be.
- davnicwil 11y ago> If someone passes in a final FooBar to a method, and you mutate the FooBar during the method for any reason, then after the method is done the object remains mutated. Fair enough, but if FooBar is your class there's an easy fix for this - make FooBar immutable. If it's not, make an immutable wrapper object for it. Very true that relying on untrue assumptions about the immutability of your data will cause hard-to-spot bugs, but the solution is then just to guarantee everything is immutable, and define that as an assumption that cannot be false if everyone on the team follows the pattern. If somebody does break the pattern, then that, rather than the race condition down the line, is the real bug. The dev who wrote the mutable data class can just have that (re-)explained to them, so it doesn't happen again.
- owlish 11y agoI used to be a fan of the Builder pattern (or Fluent Interface), but the ease of use for developers comes at the cost of no compile-time checking. Before, you just had to look up the constructor to see which parameters go where, with the benefit of type-checking. Now, you have to look up the Builder's methods to make sure you've filled in all required fields. The only case where I can actually advocate using Builders is when you have a mess of telescoping constructors.
- habosa 11y agoIf you have this problem, the library you're using is incorrectly using the Builder pattern. All required parameters should be in the constructor of the builder. Then the other methods on the builder should be optional configuration. For instance on Android you often see a builder that takes a `Context` in the constructor because that's almost always required.
- owlish 11y agoThis is contrary to the OP's example, where he passes data and num to the constructor, which is how I've seen it used frequently. The Builder pattern is often recommended to "make code easier to read" and wouldn't be useful if many/most of those parameters were required.
- EdwardDiego 11y agoIf you have required parameters in a Builder, you're doing it wrong.
- chii 11y ago> Now, you have to look up the Builder's methods to make sure you've filled in all required fields. the builder should fail with a good error message if there are required fields you didn't supply to the builder.
- owlish 11y agoThis doesn't solve the problem where the parameters aren't checked at compile-time. Now, hopefully all your codepaths are tested so your builder doesn't throw exceptions in production.
- meddlepal 11y agoSome good suggestions in here and some very very bad ones. For example, using Optional as a member type, tuples etc.
- deleted 11y ago[deleted]
- chii 11y agothis details a good amount of the problems with optional, but yet why it will still be used despite it: https://developer.atlassian.com/blog/2015/08/optional-broken/ https://developer.atlassian.com/blog/2015/08/optional-broken...
- cletus 11y agoI mostly agree with this list but some things I have issues with. The biggest is DI. Having witnessed what a tangled mess large Guice codebases can become where you really have no idea what is providing what I have to say that Spring here gets a bad rap from the all-XML days and (IMHO) it is actually the cleanest and easiest to work with. The author mischaracterizes Spring too: > It has a either code-based wiring or XML configuration-based wiring. Actually Spring can use a mix of XML code configuration and auto-wiring. A modern Spring application will have precisely an application context XML file with one line per "bean" eg: <bean class="com.foo.SomeClass" /> and that's it. Note: there's no classpath scanning. Auto-wiring takes care of the rest. This has two big advantages (over Guice in particular): 1. Everything is in one place. If it's not instantiated here it doesn't exist. Guice can be many levels deep and tends to descend into ModuleBuilders and conditional logic in modules (which the docs recommend against but tends to be used extensively anyway); and 2. Testing. Testing is incredibly easy. I'm talking functional testing here, even integration testing. You simply include another XML file that overrides any definitions from the first. Everything else remains the same. Testing with Guice can be horrendous and once you start making extensive use of Modules.override() you should abandon all hope. I'm glad to see this post recommend Maven. It's fun to bash Maven but most detractors ignore the huge benefit that applies to any shop with lots of devs: Maven is opinionated. That's a plus not a minus. Seen one Maven Web application and you've seen them all. There's no really weird directory layout because someone decided they just had to be different. Lastly Play. I really tried to like Play. I really did. But as of Play 2.0+ it became a Scala not a Java framework. It may still work with Java but some things just require you to know something about Scala. That's a huge negative IMHO. I'm not interested in Scala. Nobody is. Plus the auto-compile feature of Play tended to randomly break with indecipherable error messages for no readily apparent reasons such that you couldn't tell if it was a bug or not. I get enough of that with C++ template programming thank you. I also agree that the two books the author recommends are probably the two most important Java books in existence.
- jacques_chester 11y ago> A modern Spring application will have precisely an application context XML file with one line per "bean" eg: I prefer my Spring applications to look like this: @SpringBootApplication public class Application { ... }
- jacques_chester 11y agoI've worked on two Spring Boot projects so far and written somewhere between zero and no XML.
- davnicwil 11y agoOn the struct-like data objects part - this is so much nicer than the getter (and setter for mutable objects) pattern, just because readability is so much improved. Getters/setters is a pattern that it took me a while to reject, having always been told that public fields were really, really bad. But they're simply not. Having logic in getters/setters is the only justification for this added bloat. In my experience, in the overwhelming majority of cases (never say all) having logic in getters and setters is a clear indication that your design is flawed anyway, so most of the time you should never have them. On this part: > Further, this class is immutable unless you extend it, so we can reason about it easier as we know that it can't be changed. Well, I'd go further. Make the class final if you really want to guard against people extending it. Of course this is most applicable for writing reusable libraries where communication with consumers may be limited. If you rely on language features to enforce development practices in a team who can freely communicate, in general you've got a much bigger problems than the few rare bugs that extending an immutable class and making it mutable will cause. Obviously though, putting guard rails in can only ever be helpful, even when absolutely everyone understands that there's a cliff edge there, and wouldn't conciously try to walk over it anyway.
- mark_l_watson 11y agoI don't like to use getters, setters either. I like default package access so instance variables are just visible inside a package.
- whitenoice 11y agoFor web framework I would also suggest Dropwizard. Its simple easy to use and robust.
- mindcrime 11y agoOne thing I'd add, is that Java is very amenable to static code analysis, and there are good tools like PMD and FindBugs for Java. I always encourage teams using Java to use one or both. The biggest downside is that you usually have to tweak the rule-sets to avoid getting lots of false positives. And if you get too many false positives then people tend to stop paying attention to the notifications from the tool. On a related note, I really encourage Java teams to shoot for a goal of having compiles that finish with no warnings. If there are generic warnings littering your code (like, for example, all the warnings about unqualified references to generic types that you get when using code written before generics existed), bite the bullet and either fix them, or make it explicit that you don't care and add the appropriate annotations to turn them off. At least that way, when a new, interesting warning shows up, maybe people will pay attention to it. And yes, this is something of a "do as I say, not as I do" thing, as I have definitely written Java code that doesn't follow this rule. Especially on my personal projects or code where I'm not working with a big team. On bigger projects, I do try to push that as much as I can, depending on my role on the project.
- lomnakkus 11y ago> [...] Java is very amenable to static code analysis [...]. The biggest downside is that you usually have to tweak the rule-sets to avoid getting lots of false positives. These two statements seem to be in opposition to each other. (All your advice is spot on. If you let warnings remain in code, you'll just end up accumulating more and more warnings until it's too much to fix. I guess it's a sort of "broken windows" effect.)
- mindcrime 11y agoI don't know if they're in opposition exactly. The Java language is definitely amenable to static analysis to some extent, probably more so than a more dynamic language. But a lot of the "false positives" are just about what rules the developers of the tool ship by default, and what they define as a "bug" (or "possible bug"). For better or for worse, static analysis tools sometimes conflate simple stylistic issues ("don't use variable names less than two characters") with things that are likely to be actual logic errors. And since the tool will always be limited in how much it can understand about the intent of the code, it's pretty much always just applying heuristics and a probabilistic analysis, even when processing Java. So yeah, those tools aren't perfect, but my subjective experience has been that they help, as long as you tweak the knobs so that they don't just generate something equivalent to the "broken windows" compiler warnings, etc.
- adarshr 11y agoMy solution is to use Groovy wherever possible. It has a superb syntax, very low learning curve and compiles into JVM bytecode. There is no issue with getters and setters in Groovy - you just define the fields, which Groovy calls properties and defines implicit getters and setters. If not for the main application code, I would definitely use Groovy for unit testing in combination with Spock Framework. You just have to try it once to realise what you've been missing all these years with JUnit.
- vorg 11y agoGroovy's always been OK for quick and dirty testing of Java classes, though the statement label hack that Spock uses makes Groovy look trashy. The static compilation that came with Groovy 2.0 is what no-one should touch if they don't want to spend countless hours and days debugging and wondering why they didn't use a language built from the ground up for static compilation, e.g. Java, Scala, Kotlin, Ceylon, and others.
- jonhohle 11y agoAs a fan of immutable objects, I bemoan the builder pattern. The nice thing about constructors (or factory methods) is that you have a compile time guarantee that you have all required components to construct an object and validate its invariants. With a field oriented builder, only the documentation informs you whether you've met all of the conditions to construct an object. Also, a builder which mutates its own internal state breaks the idea of partial application where several variants can be constructed from a parent. Unrelated to builders, the stream example could be simplified further with method references final List<String> filtered = list.stream() .filter(s -> s.startsWith("s")) .map(String::toUpperCase) .collect(Collectors.toList()); It would be nice if Oracle back filled some predicates to make them more useful in streams (e.g. "s"::isPrefixOf, etc.).
- benjiweber 11y agoYou can create immutable "builder" with lambda chains. This also gives you the compiletime guarantee that you have specified the required fields (At the cost of flexibility and ease of adding new fields) http://benjiweber.co.uk/blog/2014/11/02/builder-pattern-with-java-8-lambdas/ http://benjiweber.co.uk/blog/2014/11/02/builder-pattern-with... Regarding your isPrefixOf - is there a significant benefit to that being in the standard library? You could define a isPrefixedWith("s") that returns a Predicate<String> yourself.
- hepta 11y agoThis is great, thanks. I've also used something like this: https://gist.github.com/dwijnand/3741951 https://gist.github.com/dwijnand/3741951
- jacques_chester 11y agoThe Builder pattern includes setting sensible defaults on the built object before building it. You configure the built object in the constructor of the Builder, before anyone starts updating fields. You can achieve this through constructors or setters; Builder itself is agnostic.
- based2 11y agoNothing on findbugs, checkstyle, EOL and CVEs? log4j: now log4j2 jOOQ => $, JPA, MyBatis
- tsotha 11y ago>Java 8 has a nice stream and lambda syntax. You could write code like this: final List<String> filtered = list.stream() .filter(s -> s.startsWith("s")) .map(s -> s.toUpperCase()) .collect(Collectors.toList()); >Instead of this: final List<String> filtered = new ArrayList<>(); for (String str : list) { if (str.startsWith("s") { filtered.add(str.toUpperCase()); } } >This allows you to write more fluent code, which is more readable. I don't agree. The second snipped is far more readable. This seems like an effort to use lambdas just to be using lambdas. Not only that, but the non-lambda version is far easier to maintain. When you get exceptions in code that has the form this().that().theOther() it's generally not obvious where the problem is.
- deleted 11y ago[deleted]
- ap22213 11y agoA matter of personal taste, but I greatly prefer the lambda style (though I wish the example had the .stream() on the following line). I have been coding Java (and C-style) languages for a really long time. And, I still have some cognitive delay when traversing multiple indented scopes. Not a long delay, but long enough to be slightly annoying. With the lambda style, I can quickly glance down the text column to see what's going on. Stream -> Map -> Filter -> Collect. After using lambdas enough, one starts to subconsciously cull the stream() and collect() calls. So, when I look at it, I really see Map -> Filter. But what really, really annoys me is that exception handling can't be applied to the whole stream processing statement. This leads to code that looks like: collection .stream() .map(o -> { try { return o.oToC(); } catch(Exception e) { // handle } return new C(); }) .reduce((c1, c2) -> { try { return c1.combine(c2); } catch(Exception e) { // handle } return c1; }); For that, I have had to write a bunch of helper functions that wrap and throw the exceptions.
- anotherevan 11y agoI find lauding use of Optional over null for return values, but eschewing checked exceptions somewhat inconsistent. The problem with null is it can be returned anywhere, from any function. It is difficult to generate compile time errors for if they go unhandled. But unchecked exceptions are great because you don’t have to then think about them. hmmm. Addendum: I’m also of the opinion that trying to write java code without an IDE is highly inefficient. With an IDE, you can locate all the throws you’re not taking care of quickly, and usually quickly deal with them to get things to compile. Yeah, it’s a bit of housekeeping overhead, but once they are dealt with, they are dealt with, even if it is just passing them upstream.