Я часто рекомендую Groovy's @Immutable
AST преобразование как простой способ сделать классы, ну, неизменные. Это всегда отлично работает с другими классами Groovy, но кто-то недавно спросил меня, могу ли я смешивать эти классы с Java-кодом. Я всегда считал, что ответ был «да», но я попадаю в ловушку.Groovy @Immutable классы в Java
Скажем, у меня есть непреложный User
класс:
import groovy.transform.Immutable
@Immutable
class User {
int id
String name
}
Если я проверить это с помощью теста JUnit, написанный на Groovy, все работает, как ожидалось:
import org.junit.Test
class UserGroovyTest {
@Test
void testMapConstructor() {
assert new User(name: 'name', id: 3)
}
@Test
void testTupleConstructor() {
assert new User(3, 'name')
}
@Test
void testDefaultConstructor() {
assert new User()
}
@Test(expected = ReadOnlyPropertyException)
void testImmutableName() {
User u = new User(id: 3, name: 'name')
u.name = 'other'
}
}
я могу сделать то же самое с Тест JUnit написан на Java:
import static org.junit.Assert. *;
import org.junit.Test;
public class UserJavaTest {
@Test
public void testDefaultCtor() {
assertNotNull(new User());
}
@Test
public void testTupleCtor() {
assertNotNull(new User(3, "name"));
}
@Test
public void testImmutableName() {
User u = new User(3, "name");
// u.setName("other") // Method not found; doesn't compile
}
}
Это работает, хотя на горизонте есть проблемы. IntelliJ 15 не нравится вызов new User()
, утверждая, что конструктор не найден. Это также означает, что среда IDE подчеркивает класс красным цветом, то есть имеет ошибку компиляции. Тест проходит в любом случае, что немного странно, но так оно и есть.
Если я попытаюсь использовать класс User
в Java-коде напрямую, все начинает становиться странным.
public class UserDemo {
public static void main(String[] args) {
User user = new User();
System.out.println(user);
}
}
Снова IntelliJ не устраивает, но компилируется и запускается. Выход из всех вещей:
User(0)
Это странно, потому что, хотя @Immutable
преобразование делает генерировать метод toString
, я скорее ожидал, что это выход, чтобы показать оба свойства. Тем не менее, это может быть связано с тем, что свойство name
равно null, поэтому оно не включено в вывод.
Если я пытаюсь использовать конструктор кортежа:
public class UserDemo {
public static void main(String[] args) {
User user = new User(3, "name");
System.out.println(user);
}
}
Я получаю
User(0, name)
как выход, по крайней мере, на этот раз (иногда не работает вообще).
Затем я добавил файл сборки Gradle. Если я ставлю Groovy классы под src\main\groovy
и классы Java под src\main\java
(то же самое для испытаний, но используя папку test
вместо этого), я сразу получаю вопрос компиляции:
> gradle test
error: cannot find symbol
User user = new User(...)
^
Я обычно исправить проблемы кросс-компиляции, как это пытаясь использовать компилятор Groovy для всего. Если я поместил оба класса под src\main\java
, ничего не изменится, что не является большим сюрпризом. Но если я ставлю оба класса под src\main\groovy
, то я получаю это во время compileGroovy
фазы:
> gradle clean test
error: constructor in class User cannot be applied to the given types;
User user = new User(3, "name");
required: no arguments
found: int,String
reason: actual and formal arguments differ in length
Ха. На этот раз он возражает против конструктора кортежей, потому что он считает, что он имеет только конструктор по умолчанию. Я знаю, что преобразование добавляет конструктор по умолчанию, основанный на карте и конструктор кортежей, но, возможно, они не генерируются вовремя, чтобы код Java их видел.
Кстати, если я снова отделить Java и Groovy классы, и добавьте следующие строки в моем Gradle построить:
sourceSets {
main {
java { srcDirs = []}
groovy { srcDir 'src/main/java' }
}
}
я получаю ту же ошибку. Если я не добавлю блок sourceSets
, я получаю User
класс, не найденный ранее.
Таким образом, нижняя строка - это правильный способ добавить класс @Immutable
к существующей системе Java? Есть ли способ получить конструкторы, которые будут созданы вовремя для Java, чтобы увидеть их?
Я делаю презентации Groovy разработчикам Java уже много лет и говорю, что вы можете это сделать, только чтобы столкнуться с проблемами. Пожалуйста, помогите мне как-нибудь спастись. :)
IDEs имеют тенденцию давать ложные отрицания, когда дело доходит до Groovy, особенно с трансформациями AST. Для проблемы кросс-компиляции вы можете попробовать два отдельных проекта: один java и один groovy. Если это еще нерешенный вопрос, я дам ему вихрь по утрам. Это действительно похоже, что он должен работать. – cjstehno
Является ли поведение одинаковым с '@ CompileStatic'? – dmahapatro
'@ CompileStatic' ничего не меняет – kousen