{{notification.text}}

MirGames

Xtend - это маленький язык над Java, позволяющий писать чуть более лаконично и красиво. Язык именно над Java, а не над JVM и компилируется, непосредственно в Java-код (а не byte-код). Построен на основе Xtext, поэтому имеет отличную поддержку IDE, отладчика и другие, стандартные для Eclipse фичи.

Xtend Intro

Плюсы

В очень <a href="http://www.eclipse.org/xtend/documentation.html">небольшой документации написаны преимущества языка:

Extension methods

Пополнение закрытых типов новой функциональностью:
"hello".toFirstUpper() // вызовет toFirstUper("hello")

Лямбда-выражения

Понятный синтаксис для анонимных функций. Пример со Swing:

val textField = new JTextField();
textField.addActionListener([ e |
textField.text = "Что-то произошло!" ])

Вместо:

final JTextField textField = new JTextField();
textField.addActionListener(new ActionListener() {
@Override public void actionPerformed(ActionEvent e) { textField.setText("Something happened!"); } });

В данном примере лямбды работают так. Если функция (addActionListener в данном случае) принимает в себя интерфейс (ActionListener), у которого надо реализовать один единственный метод (actionPerformed), то наша лямбда и будет реализацией этого метода, а весь остальной код Xtend сгенерирует сам.

Однако стоит уточнить, что реализовать интерфейс/абстрактный класс непосредственно на месте его использования, Xtend уже не позволяет.

Перегрузка операторов - дабы была возможность сделать свои библиотеки более выразительными

val x = 2.71BD
val y = 3.14BD
val sum = x + y // вызовет BigDecimalExtension.operator_plus(x,y)

Нечего добавить.

Мощный оператор switch

switch myString {
case myString.length>5 : "Это длинная строка." case 'какая-то' : "Какая-то строка." default : "Это вообще левая строка." }

В котором еще есть и type guards:
def length(Object x) {
switch x { String case x.length > 0 : x.length // length is defined for String List<?> : x.size // size is defined for List default : -1 } }

Множественная диспетчеризация (полиморфный вызов методов)

Пример из документации, имхо, несовсем понятный, поэтому приведу свой. Есть два класса:
class A{};
class B extends A{};

И один метод, который должен по-разному работать с этими двумя классами. Причём не на общем уровне (не на уровне базового класса), а на максимально конкретном. Т.е. у нас есть очередь из движущихся объектов. Теперь нужно работать с людьми, машинками, паровозиками и т.д. персонально.
def m(A a){
println("Пришёл объект класса A"); } def m(B b){
println("Пришёл объект класса B"); }

Теперь создаём объекты и вызываем методы:
val A a = new A
val A b = new B
m(a); m(b);

В обоих случаях выведется, что пришёл объект класса A;

Однако, если бы мы объявили методы, как dispatch-методы, то такого бы не произошло:
def dispatch m(A a){
println("Пришёл объект класса A"); } def dispatch m(B b){
println("Пришёл объект класса B"); }

И всё вывелось бы правильно. Кстати говоря, наши функции m переименуются в _m, а также добавиться сама функция m, имеющая следующий вид:
public static String m(final A b) {
if (b instanceof B) { return _m((B)b); } else if (b != null) { return _m(b); } else { throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object>asList(b).toString()); } }

Пожалуй, это всё о диспетчеризации.

Шаблоны (с интеллектуальной обработкой пробелов)

def someHTML(List<Paragraph> paragraphs) '''
<html> <body> «FOR p : paragraphs» «IF p.headLine != null»

«p.headline»

«ENDIF»

«p.text»

«ENDFOR» </body> </html> '''

Тут я бы хотел особо отметить, что у такие шаблоны никак не портят оформление основного кода. Т.е. если у вас сама функция уже на 10 табов съезжает вправо и вы начинаете писать вот такой вот шаблон, то сам шаблон, при выводе, вправо не уедет - все табы, предназначенные для форматирования кода (а не шаблона) будут проигнорированы и в шаблон добавлены не будут.

Нет операторов, есть выражения

def three(){
3 }

Свойства объектов

myObj.myProperty // myObj.getMyProperty() (в случае, если myObj.myProperty не находится в области видимости.)

Казалось бы, свойства хорошо бы использовать вот в таком стиле:
window.size += 2;
Ан-нет! Не получится, потому что по каким-то непонятным причинам, +=, -=, ++, -- лично мне использовать не получается - вылетает с ошибкой.

Скрытые переменные this и it

То, что this можно опустить и писать вместо this.move, this.run просто move и run, соответственно, это понятно. Но есть также ключевое слово it. Пример:
val it = new Person
eat; prepare; run;

it также можно передавать в лямбды:
socket.onReceive[it|
//К нам в it пришёл удалённый socket send("ABC"); ]

Также, it можно ставить в функции, которая вызывает лямбду. Грубо говоря:
html[
//Вот этот верхний html-объект перед вызовом лямбды //поместит себя в it, поэтому сейчас вызовется html.body() body[ //А body установит в it себя, поэтому //вызовется html.body.p p("Paragraph") ] ]
Пример не высосан из пальца:

Прочие прелести
  • Вывод типов переменных и возвращаемых значений функций
  • Полная поддержка Java-генериков
  • Трансляция в Java-код, а не JVM byte-code, поэтому стоит ожидать хорошей поддержки Android, GWT и других платформ. Кстати говоря, у Андроида возникают проблемы с отладкой. Лично не проверял, но, говорят, что на Андроиде отладчик выкидывает нас в Java, а не Xtend-код.
  • myRef?.doStuff переходит в код вида: if(myRef != null) myRef.doStuff()
  • Можно писать все классы в одном файле :)
  • Есть псевдо-аннотация @Data, которую, если добавить перед классом, то классу автоматом генерируется hashCode(), equals(), toString(), все поля объявляются финальными, а также генерируются getter'ы, setter'ы и удобный конструктор.

Минусы

Если подытожить минусы из вышесказанного текста, то получится список:

  • Нету оператов +=, -=. Как я понимаю, это бага и скоро это исправится. Тем более, что неудобств не вызывает.
  • При использовании лямбд используются только интерфейсы. Абстрактные классы не катят, поэтому существующие библиотеки (например, SWT) должны быть дополнительно адаптированы
  • Нельзя объявить интерфейс в Xtend-коде. По сути, это почти не недостаток, т.к., если генерировать их средой - то вообще без разницы, на каком языке их генерировать
  • Нельзя объявить реализацию класса на месте
  • Импорт плейсхолдерами считается устаревшим
  • Иногда (совсем редко) отладчик вылетает в Java-код. Впрочем, Java-код - это не ассемблер и его вполне можно отлаживать.
  • Нету классического for'а. А ведь так хочется.

Хвастовство :)

Есть sinatra-like либа spark. Позволяет писать такой код:
import static spark.Spark.;
import spark.
;

public class HelloWorld {
public static void main(String[] args) { get(new Route("/hello") { @Override public Object handle(Request request, Response response) { return "Hello World!"; } }); } }
Тут идёт перегрузка и, как следствие, лямбды тут не запашут :(

С вот таким небольшим допилом:

//Объявляем интерфейс, чтобы можно было использовать лямбды package src;

import spark.Request;
import spark.Response;

public interface _Route {
public Object handle(Request request, Response response) ; }

И еще вот таким:

package src;

import static spark.Spark.get;
import spark.Request;
import spark.Response;
import spark.Route;

public class Support {
public static void gets(String route, final _Route callback){ get(new Route(route) { @Override public Object handle(Request request, Response response) { return callback.handle(request, response); } }); } }

Теперь можно писать вот такой код:

package src;

import static src.Support.*;

class Application {
def static void main(String[] args){ gets("/")[r1, r2| "Hello" ]

    gets("/world")[r1, r2|
        "world"
    ]
}

}

Ну не няшно ли?

10.12.12 14:53

Комментарии

11.12.12 01:23

Выглядит как не всегда удачная попытка перенести фичи из C# в Java. :-)

11.12.12 01:33
Можно писать все классы в одном файле :)

А вот это они, кстати, зря :-)<br/>
<br/>
Вообще из Java мира мне больше MPL от JetBrains нравится. Возможность создавать новые абстракции на уровне языка меня всегда прельщала. Я даже уже начал проникаться лиспом)

11.12.12 01:38

Не понял :) Так тебе понравился язык или нет? :)<br/>
Завтра придется попробовать его на Android'е, расскажу, что получилось :)

11.12.12 02:57

На самом деле я предпочел бы, чтобы этот язык компилировался в байт-код, а не транслировался в Java. На мой взгляд это избыточно.

11.12.12 03:31

Ну, так он и компилируется в байт-код в конечном счёте :) Если тебя не интересует Java-код, то какая разница?<br/>
+ для байт-кода есть же Scala.

({{comment.CreationDate | date:'dd.MM.yy HH:mm'}})
Отредактировано: {{comment.UpdatedDate | date:'dd.MM.yy HH:mm'}}