Tutorial Make everything simpler in Java 8!

Discussion in 'Resources' started by pie_flavor, Feb 16, 2015.

Thread Status:
Not open for further replies.
  1. Offline

    pie_flavor

    Java 8 is a wonderful update, and it's a real shame that most server hosts still use 6. Most people, though, have no idea how to use the wonderful features in Java 8 in order to simplify plugins!
    In this tutorial I will be covering how to use a new feature of Java 8, called lambdas, which make life a lot easier.

    Consider, if you will, the CommandExecutor interface. It has only one undefined method, the onCommand(CommandSender, Command, String, String[]). Most people, for each individual command, create separate files for each command. Others put all commands in one big if/then sequence in the main class. Others do a combination of both, if/then sequences in separate executors to combine multiple commands.
    And the rest, whose method is important here, go
    Code:
    getCommand("mycommand").setExecutor(new CommandExecutor() {
      public boolean onCommand(/*...*/) {
    One can very easily create new classes that way, called anonymous inner classes. They are created on the fly, with no name, and work like a charm.
    Looks a bit ugly though. What you can do in Java 8 is this:
    Code:
    public void onEnable() {
        getCommand("mycommand").setExecutor(this::myCommand);
        getCommand("myothercommand").setExecutor(this::otherCommand);
    }
    public boolean myCommand(CommandSender sender, Command cmd, String label, String[] args) {
    //...
    }
    public boolean otherCommand(CommandSender sender, Command cmd, String label, String[] args) {
    //...
    }
    Nope, that's not a syntax error. By typing an instance of a class, then the separator '::', and the name of a method without the parentheses, you can instantly generate an anonymous inner class, where the one undefined method is the method you define. You can have multiple methods in one class, each with a separate command.

    Now, what if you need to define custom arguments? What if you want to do the first thing (new CommandExecutor() {}) but simpler? There's actually 2 ways of doing lambdas. The first is <instance>::<method>.
    The second is slightly more complicated, but is still simpler than the above methods.
    Let's say I want to make a very simple command that'll test for an item in a player's inventory. I can do this in Java 8:
    Code:
    getCommand("testforitem").setExecutor((sender, cmd, label, args) -> (( sender instanceof Player && args.length >= 2) && Bukkit.getPlayer(args[0]).getInventory().contains(Material.getMaterial(args[1]))));
    Using this syntax:
    Code:
    (arg0, arg1, etc.) -> {method body}
    I can create another of those anonymous classes, but this time taking away not only the class name, but the method name. You don't even need to say what type the arguments are. If the method you are doing is void, you can put a void call in a set of parentheses (or out of them, your choice). If it returns something, you can put the call that returns the correct type where I did. Anything else requires braces and, if applicable, a return statement.
    Remember, this only works if the interface or abstract class has only one undefined/abstract method. Any more and you'll have to go the old route.

    Some (useless but displaying) applications of this:
    Code:
    public void onEnable() {
        getCommand("mycommand").setExecutor(this::myCommand);
        getCommand("mycommand").setTabCompleter(this::tabMyCommand);
    }
    public List<String> tabMyCommand(CommandSender sender, Command cmd, String label, String[] args) {
        List<String> list = new ArrayList<String>();
        list.add("diamond");
        return list;
    }
    public boolean myCommand(CommandSender sender, Command cmd, String label, String[] args) {
        sender.sendMessage("§bDiamonds!");
        return true;
    }
    Code:
    public void randomize(Player p) {
        Random r = new Random();
        p.sendMessage(r.nextInt(300));
    }
    @EventHandler
    public void onJoin(PlayerLoginEvent e) {
        Bukkit.getScheduler().runTaskTimer(this, () -> randomize(e.getPlayer()), 6000L, 6000L);
    }
    Possibilities are endless. As you can see in example 2, I was able to pass an argument to a simulated Runnable. You can't do this normally.
    Hope this helps new developers!
     
    Last edited by a moderator: Feb 16, 2015
  2. Offline

    Regablith

    Awesome! Thanks!
     
  3. Offline

    T0pAz7

    Welcome to the amazing world of lambda and anonymous class.
     
  4. Offline

    pie_flavor

    I posted a link to a hosting company that actually ran java 8. Wonder what happened to it.
     
  5. Offline

    teej107

    Most servers use Java 7 (I think). And building plugins with Java 8 will be useless for servers running 7 or 6.
     
  6. Offline

    pie_flavor

    @teej107 Most hosts use Java 6, the ones who don't, use Java 8. The people who actually took the step up from 6 to 7 made the same step from 7 to 8.
     
  7. Offline

    teej107

    The host I'm using is still using 7. If most hosts are still below 8, it's still not a good idea to build with 8 if your planning on releasing your plugin to the public.
     
  8. Offline

    Cirno

    Going to back @teej107
    Coding your plugins in Java 8 is a bad idea because most servers don't run Java 8 yet. Java 6 is prefered since you get the lower-end server-base; Java 7 if you want to build for most "standard" servers.
     
  9. Offline

    RawCode

    making code lines with lenght over 70 does not make things any simple.
    making plugin that wont run on most servers is not simple for noob hosters who will post "error reports".

    labda code is syntax sugar, it will compile into classes and methods, it can be "simple" only for limited set of applications, in other cases it will make code actually harder to read and follow.
     
  10. Offline

    pie_flavor

    @RawCode This isn't about decompilation; it'll make the code easier to write. @teej107 @Cirno Java 6 has no advantage whatsoever over Java 7, and likewise 7 over 8. triangle.gs uses Java 8, and someone who port-forwards can choose for themselves.
    What exactly does that mean?
     
  11. Offline

    teej107

    @pie_flavor Not everybody uses triangle.gs. Each Java update has its advantages over the last.
    If this is a private plugin then sure! Why not compile with 8? I'm not saying 8 is terrible, but lower Java versions can't use it.
     
  12. Offline

    Cirno

    So you're saying that you'll only focus on Java 8 even if the vast majority of servers still use Java 6?
     
  13. Offline

    pie_flavor

    @Cirno What? No, I just posted a tutorial on an amazing new feature of Java 8. It's the vast majority's own fault they use 6.
     
  14. Offline

    Cirno

    So you're going to assume the reason why the "vast majority" still uses Java 6 is purely because of their fault and not because their server host hasn't updated? The point I'm trying to make is that you shouldn't use Java 8 features unless you are willing to somehow compensate for those who still run Java 7 or 6; public plugins shouldn't be released matching your environment; it should be able to cover a vast amount of environments. The original slogan of Java is to "write once, run everywhere" (albeit; there's a few exceptions i.e native code or interacting with the OS in a more "direct" fashion).

    There is some good news revolving around this issue though:
    JEP 238 provides the ability to store multiple variants of compiled Java classes and thus allowing multiple versions to be contained within a single Jar :p
     
  15. Offline

    pie_flavor

    @Cirno okay, seriously, what's your problem? I mean like what the hell. I post a tutorial of a cool feature in Java 8, and you attack me saying that I'm being unfair to the people of Java 7, and the people who use hosts that use Java 6. It's a tutorial, man, not an attack on society. And the 'write once, run everywhere' thing doesn't mean versions. I mean, that's like telling people to only write plugins for 1.7.10 because not that many people use Spigot 1.8.
     
  16. Offline

    teej107

    @pie_flavor Calm down! It is a pretty good tutorial, but if I start seeing more UnsupportedClassVersionError problems in the Plugin Development section, don't act like you aren't partially responsible.
     
  17. Offline

    pie_flavor

    @teej107 sorry, overreacted. he is being kind of rude though.
    Also, I initially compile all my plugins for requests in Java 8 and only fix them if they ask me to. Of the 30 or so requests I've filled, I've gotten like 4 complaints about class version errors.
     
  18. Offline

    xTrollxDudex

    Actually, quite a few servers are using Java 8 according to McStats, however, the vast majority use Java 7. Very few actually use Java 6, but it seems like a lot because plugin developers don't know how to install Java.
     
    TigerHix and Hawktasard like this.
Thread Status:
Not open for further replies.

Share This Page