Kotlin имеет краткий синтаксис для объявления классов данных:
data class User(val name: String, val age: Int)
Эквивалентный синтаксис Java является подробным. Вы должны создать класс Java с закрытыми полями. И методы получения и установки для полей. И дополнительные методы, такие как equals() , hashCode() и toString() .
Но кто сказал, что вы должны создавать Java-код вручную? В этой статье я покажу вам, как создавать исходные файлы Java из файла YAML .
Вот пример файла YAML:
User:
name: Name
age: Integer
Name:
firstName: String
lastName: String
Примером вывода генератора кода являются два исходных файла Java, User.java и Name.java .
Содержание User.java :
public class User{
private Name name;
private Integer age;
public User(){
}
public Name getName(){
return name;
}
public void setName(Name name){
this.name = name;
}
public Integer getAge(){
return age;
}
public void setAge(Integer age){
this.age = age;
}
}
Name.java аналогично.
Суть этой статьи в том, что вы узнаете, как запрограммировать генератор кода с нуля. И его легко адаптировать к вашим потребностям.
Метод main() выполняет две задачи:
- Шаг 1: Прочитайте в файле YAML спецификации класса
- Шаг 2: Создайте исходные файлы Java на основе спецификаций класса
Это отделяет чтение от генерации. Таким образом, вы можете изменить формат ввода в будущем или поддерживать другие форматы ввода.
Вот метод main() :
public static void main(String[] args) throws Exception {
// Make sure there is exactly one command line argument, the path to the YAML file
if (args.length != 1) {
System.out.println("Please supply exactly one argument, the absolute path of the YAML file.");
return;
}
// Get the YAML file's handle, and the directory it's contained in
// (generated files will be placed there)
final String yamlFilePath = args[0];
final File yamlFile = new File(yamlFilePath);
final File outputDirectory = yamlFile.getParentFile();
// Step 1: Read in the YAML file, into class specifications
YamlClassSpecificationReader yamlReader = new YamlClassSpecificationReader();
List classSpecifications = yamlReader.read(yamlFile);
// Step 2: Generate Java source files from the class specifications
JavaDataClassGenerator javaDataClassGenerator = new JavaDataClassGenerator();
javaDataClassGenerator.generateJavaSourceFiles(classSpecifications, outputDirectory);
System.out.println("Successfully generated files to: " + outputDirectory.getAbsolutePath());
}
Позвольте мне объяснить, что происходит в этой строке:
ListclassSpecifications = yamlReader.read(yamlFile);
Спецификация класса – это определение создаваемого класса и его полей. Помните User в примере файла YAML?
User:
name: Name
age: Integer
Когда YAML reader прочитает это, он создаст один Class Specification объект с именем User . И эта спецификация класса будет ссылаться на два Спецификация поля объекты, вызываемые имя и возраст .
Код для Class Specification class и Field Specification class прост.
Содержание ClassSpecification.java :
public class ClassSpecification {
private String name;
private List fieldSpecifications;
public ClassSpecification(String className, List fieldSpecifications) {
this.name = className;
this.fieldSpecifications = fieldSpecifications;
}
public String getName() {
return name;
}
public List getFieldSpecifications() {
return Collections.unmodifiableList(fieldSpecifications);
}
}
Содержание Содержание
public class FieldSpecification {
private String name;
private String type;
public FieldSpecification(String fieldName, String fieldType) {
this.name = fieldName;
this.type = fieldType;
}
public String getName() {
return name;
}
public String getType() {
return type;
}
}
Единственный оставшийся вопрос для шага 1: как вы получаете доступ из файла YAML к объектам этих классов?
Программа чтения YAML/| использует библиотеку SnakeYaml для анализа файлов YAML. SnakeYaml делает содержимое файла YAML доступным в структурах данных, таких как карты и списки. Для этой статьи вам нужно только разобраться в картах. Потому что это то, что мы используем в файлах YAML.
Посмотрите на пример еще раз:
User:
name: Name
age: Integer
Name:
firstName: String
lastName: String
То, что вы видите здесь, – это две вложенные карты. Ключом внешней карты является имя класса (например, User ). Когда вы получите значение для Пользователь ключ, вы получаете карту полей класса:
name: Name
age: Integer
Ключом этой внутренней карты является имя поля, значением – тип поля.
Это сопоставление строк с сопоставлением строк со строками. Это важно для понимания кода программы чтения YAML . Вот метод, который считывает полное содержимое файла YAML:
private Map> readYamlClassSpecifications(Reader reader) { Yaml yaml = new Yaml(); // Read in the complete YAML file to a map of strings to a map of strings to strings Map > yamlClassSpecifications = (Map >) yaml.load(reader); return yamlClassSpecifications; }
Используя спецификации класса yaml в качестве входных данных, программа чтения YAML создает объекты Спецификации класса :
private ListcreateClassSpecificationsFrom(Map > yamlClassSpecifications) { final Map > classNameToFieldSpecificationsMap = createClassNameToFieldSpecificationsMap(yamlClassSpecifications); List classSpecifications = classNameToFieldSpecificationsMap.entrySet().stream() .map(e -> new ClassSpecification(e.getKey(), e.getValue())) .collect(toList()); return classSpecifications; }
Метод create Class Name To Field Specifications Map() создает
- спецификации полей для каждого класса и на основе этих
- сопоставление имени каждого класса со спецификациями его полей.
Затем/| YAML reader создает Class Specification объект для каждой записи на этой карте.
Содержимое файла YAML теперь доступно для шага 2 независимым от YAML способом. Мы закончили с шагом 1.
Apache FreeMarker – это движок шаблонов Java, который создает текстовые выходные данные. Шаблоны написаны на языке шаблонов FreeMarker (FTL). Это позволяет смешивать статический текст с содержимым объектов Java.
Вот шаблон для создания исходных файлов Java, класс данных java.ftl :
public class ${classSpecification.name}{
<#list classSpecification.fieldSpecifications as field>
private ${field.type} ${field.name};
public ${classSpecification.name}(){
}
<#list classSpecification.fieldSpecifications as field>
public ${field.type} get${field.name?cap_first}(){
return ${field.name};
}
public void set${field.name?cap_first}(${field.type} ${field.name}){
this.${field.name} = ${field.name};
}
}
Давайте посмотрим на первую строку:
public class ${classSpecification.name}{
Вы видите, что он начинается со статического текста объявления класса: публичный класс . Интересный момент находится посередине: ${class Specification.name }
Когда Freemarker обрабатывает шаблон, он обращается к объекту class Specification в своей модели. Он вызывает для него метод getName() .
Как насчет этой части шаблона?
<#list classSpecification.fieldSpecifications as field>
private ${field.type} ${field.name};
Сначала Freemarker вызывает class Specification.getFieldSpecifications() . Затем он выполняет итерацию по спецификациям полей.
И последнее. Эта строка немного странная:
public ${field.type} get${field.name?cap_first}(){
Допустим, поле примера равно age: Целое число (в YAML). Freemarker переводит это в:
public Integer getAge(){
Итак ? cap_first означает: заглавную первую букву, так как файл YAML содержит age строчными буквами.
Хватит о шаблонах. Как вы создаете исходные файлы Java? Во-первых, вам необходимо настроить FreeMarker, создав экземпляр Configuration . Это происходит в конструкторе Java Data Class Generator :
public JavaDataClassGenerator() throws IOException {
configuration = new Configuration(Configuration.VERSION_2_3_28);
// Set the root of the class path ("") as the location to find templates
configuration.setClassLoaderForTemplateLoading(getClass().getClassLoader(), "");
configuration.setDefaultEncoding("UTF-8");
configuration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
configuration.setLogTemplateExceptions(false);
configuration.setWrapUncheckedExceptions(true);
}
Для создания исходных файлов/| Java Data Class Generator перебирает спецификации классов и генерирует исходный файл для каждого:
public void generateJavaSourceFiles(CollectionclassSpecifications, File yamlFileDirectory) throws Exception { Map freemarkerDataModel = new HashMap<>(); // Get the template to generate Java source files Template template = configuration.getTemplate("javadataclass.ftl"); for (ClassSpecification classSpecification : classSpecifications) { // Put the classSpecification into the data model. // It can be accessed in the template through ${classSpecification} freemarkerDataModel.put("classSpecification", classSpecification); // The Java source file will be generated in the same directory as the YAML file File javaSourceFile = new File(yamlFileDirectory, classSpecification.getName() + ".java"); Writer javaSourceFileWriter = new FileWriter(javaSourceFile); // Generate the Java source file template.process(freemarkerDataModel, javaSourceFileWriter); } }
И это все.
Я показал вам, как создать генератор исходного кода Java на основе файлов YAML. Я выбрал YAML, потому что его легко обрабатывать. И, таким образом, легко обучаем. Замените его другим форматом, если сочтете нужным.
Вы можете найти полный код на Github .
Чтобы сделать код максимально понятным, я воспользовался несколькими сокращениями:
- Нет таких методов, как
equals(),hashCode()иtoString() - Отсутствие наследования классов данных
- Сгенерированные классы Java находятся в пакете по умолчанию
- Выходной каталог совпадает с входным каталогом
- Обработка ошибок не была в центре моего внимания
Готовое к производству решение должно было бы решить эти проблемы. Кроме того, для классов данных альтернативой является Project Lombok .
Так что думайте об этой статье как о начале, а не как о конце. Представьте себе, что это возможно.
Несколько примеров:
- Классы сущностей Scaffold JPA или репозитории Spring
- Создайте несколько классов из одной спецификации на основе шаблонов в вашем приложении
- Генерировать код на разных языках программирования
- Подготовка документации
В настоящее время я использую этот подход для перевода требований к естественному языку непосредственно в код в исследовательских целях. Что ты будешь делать?
Оригинал: “https://dev.to/bertilmuth/generating-data-classes-in-java-4cef”