java - Java中的“特殊属性/属性”而不是getter/setter,以避免样板代码

标签 java reflection properties getter-setter lombok

介绍

我正在开发一个开源项目Treez,在其中以树状视图组织所谓的“原子”。这些Atom有时具有很多属性,并且可以通过树视图中的用户操作或通过Eclipse代码编辑器中的API来修改这些属性。

我的Atom本身的属性由可重复使用的“ AttributeAtoms”表示。那些拥有实际属性值并提供诸如验证之类的其他功能(“ Atom”的其他可能术语可能是“ widget”,“ bean”,“ property”或“ tree node”)。

问题

过去,我为每个Atom属性提供了一个getter / setter对。这是很多额外的工作,并且炸毁了我的Atom类的大小(请参见下面的代码示例)。现在,我正在寻找一种替代解决方案,


减少了创建新原子的工作量(并减少了维护它们的工作量)。
避免使用“冗余的”吸气剂/设定剂样板代码。


我将在下面描述一些选项。您将使用哪个选项?您是否有关于如何改善这些选择的建议?您还知道其他选择吗?

Getter / Setter代码示例

    private AttributeAtom<String> myAttribute = new FilePathAttributeAtom("myAttribtue");

    public String getMyAttribute() {
        return myAttribute.getValue();
    }    

    public void setMyAttribute(String value) {
        this.myAtrribute.setValue(value);
    }


相关文章


does java have something similar to C# properties?
(no) Properties in Java?
http://blog.netopyr.com/2011/05/19/creating-javafx-properties/
http://www.eclipse.org/forums/index.php/t/781816/
Why use getters and setters?
What is the advantage of having a private attribute with getters and setters?


考虑的选项

A.使用IDE自动生成的获取器/设置器

Eclipse提供了自动生成getter / setter的可能性。


由于getter / setter代码看起来略有不同,因此不适用于我的AttributeAtom。
不要避免多余的“冗余”代码。


如果我决定保留getter / setter,可以尝试为AttributeAtoms创建类似的内容。另请参阅有关JavaFx属性的自动获取/设置创建的文章(不起作用):
http://www.eclipse.org/forums/index.php/t/781816/

B.生成吸气剂/定型剂的注释(龙目岛计划)

龙目岛(Lombok)提供了使用注释自动生成getter和setter的可能性。


都不适合我的AttributeAtoms
我试图在Eclipse中使用Lombok。编辑器中的代码完成有效,但是我收到“未找到方法”警告。我可能需要花费更多的时间来使Lombok处理经典属性。
另请参见Is it safe to use Project Lombok?


如果我决定使用注释来定义getter / setter,则有可能扩展Lombok以使其适用于我的AttributeAtom。


此处已存在扩展Lombok的JavaFx属性的请求:https://groups.google.com/forum/#!searchin/project-lombok/getter$20and$20setter$20for$20properties/project-lombok/Ik6phxDXHVU/zzDkC2MpmvgJ
这是有关如何使用自定义转换扩展Lambok的介绍:
http://notatube.blogspot.de/2010/12/project-lombok-creating-custom.html


C.所有属性的一个通用获取器/设置器

我可以对所有的Atom属性使用单个getter / setter对

Object get(String attributeName)
void set(String attriuteName, Object value)



可以通过传递其他类型参数来提高类型安全性。
但是,我的Atom的代码完成仅会建议单个getter / setter,而用户将看不到哪些属性可用。 (也许可以通过使用Enums而不是String来标识属性来解决。但是需要以某种方式创建这些Enums。另请参见下一个选项。)


D.自定义Eclipse编辑器和代码处理

也许我可以为我的开源项目编写一个额外的Eclipse插件,通过为代码完成建议相应的伪造方法来“允许访问私有属性”。在编译用户源代码之前,假调用如

myAtom.setMyAttribue(newValue);


将被转换为真正存在的通用getter(选项C)的代码:

myAtom.set("myAttribute", newValue);


E.公共属性

如果我公开我的Atom属性,则不需要每个Atom中的getter / setter代码。而是,可重用的AttributeAtoms将提供get / set方法。用法例如看起来像这样

myAtom.myAttribute.get();
myAtom.myAttribute.set(newValue);


代替

myAtom.getMyAttribute();
myAtom.setMyAttribute(newValue);


一些缺点:


用户需要习惯这种“非常规方法”。 Java用户可能期望setMyAttribute(newValue),而C#用户可能期望myAtom.myAttribute = newValue
可以交换整个AttributeAtom,但我不想允许它:

myAtom.myAttribute = completelyDifferentAttribute



有什么改善策略吗?


有没有办法允许访问属性的方法而又不允许交换属性本身?我需要一个新的访问修饰符,例如

private *publicMethodAccess* AttributeAtom<String> myAttribute;



原子代码示例

这是一个示例Atom类。如果滚动到底部,您将发现getter / setter消耗的许多代码行。这是丑陋的,不是吗?

package org.treez.results.atom.probe;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;
import org.eclipse.swt.graphics.Image;
import org.treez.core.atom.attribute.AttributeRoot;
import org.treez.core.atom.attribute.ModelPath;
import org.treez.core.atom.attribute.ModelPathSelectionType;
import org.treez.core.atom.attribute.Section;
import org.treez.core.atom.attribute.base.AttributeAtom;
import org.treez.core.atom.variablerange.VariableRange;
import org.treez.core.data.column.ColumnType;
import org.treez.data.column.Columns;
import org.treez.data.output.OutputAtom;
import org.treez.data.table.Table;
import org.treez.results.Activator;

/**
 * Collects data from a sweep and puts it in a single (probe-) table. That table can easier be used to produce plots
 * than the distributed sweep results.
 */
public class SweepProbe extends AbstractProbe {

    /**
     * Logger for this class
     */
    @SuppressWarnings("unused")
    private static Logger sysLog = Logger.getLogger(SweepProbe.class);

    //#region ATTRIBUTES

    private AttributeAtom<String> xLabel;

    private ModelPath xRange;

    private AttributeAtom<String> yLabel;

    private AttributeAtom<String> firstFamilyLabel;

    private ModelPath firstFamilyRange;

    private AttributeAtom<String> secondFamilyLabel;

    private ModelPath secondFamilyRange;

    private AttributeAtom<String> probeName;

    private ModelPath sweepOutputModel;

    private ModelPath firstProbeTable;

    private AttributeAtom<String> probeColumnIndex;

    private AttributeAtom<String> probeRowIndex;

    //#end region

    //#region CONSTRUCTORS

    /**
     * Constructor
     *
     * @param name
     */
    public SweepProbe(String name) {
        super(name);
        createPropertyModel();
    }

    //#end region

    //#region METHODS

    /**
     * Creates the model for the property control
     */
    private void createPropertyModel() {

        //root
        AttributeRoot root = new AttributeRoot("root");

        //page
        org.treez.core.atom.attribute.Page page = root.createPage("page");

        //x section
        Section xSection = page.createSection("xSection", "X");
        xSection.createSectionAction("action", "Run probe", () -> execute(treeViewRefreshable));

        xLabel = xSection.createTextField("xLabel", "Label for x-Axis", "x");

        xRange = xSection.createModelPath("xRange", "Range for x-Axis", "", VariableRange.class, this);
        xRange.setSelectionType(ModelPathSelectionType.FLAT);
        xRange.setValue("root.studies.sweep.threshold");

        //y section
        Section ySection = page.createSection("ySection", "Y");
        yLabel = ySection.createTextField("yLabel", "Label for y-Axis", "y");

        //first family section
        Section firstFamilySection = page.createSection("firstFamily", "First family");
        firstFamilySection.setExpanded(false);

        firstFamilyLabel = firstFamilySection.createTextField("firstFamilyLabel", "Label for first family", "family1");

        firstFamilyRange = firstFamilySection.createModelPath("firstFamilyRange", "Range for first family", "",
                VariableRange.class, this);

        //second family section
        Section secondFamilySection = page.createSection("secondFamily", "Second family");
        secondFamilySection.setExpanded(false);

        secondFamilyLabel = secondFamilySection.createTextField("secondFamilyLabel", "Label for second family",
                "family2");

        secondFamilyRange = secondFamilySection.createModelPath("secondFamilyRange", "Range for second family", "",
                VariableRange.class, this);

        //probe section
        Section probeSection = page.createSection("probe", "Probe");

        probeName = probeSection.createTextField("propeName", "Name", "MyProbe");

        sweepOutputModel = probeSection.createModelPath("sweepOutput", "SweepOutput", "", OutputAtom.class, this);

        firstProbeTable = probeSection.createModelPath("tablePath", sweepOutputModel, Table.class);
        firstProbeTable.setLabel("First probe table");

        probeColumnIndex = probeSection.createTextField("probeColumnIndex", "Column index", "0");

        probeRowIndex = probeSection.createTextField("probeColumnIndex", "Row index", "0");

        setModel(root);

    }

    /**
     * Provides an image to represent this atom
     */
    @Override
    public Image provideBaseImage() {
        Image baseImage = Activator.getImage("sweep.png");
        return baseImage;
    }

    //#region CREATE TABLE COLUMNS

    /**
     * Creates the required columns for the given table
     *
     * @param table
     */
    @Override
    protected void createTableColumns(Table table) {
        //TODO

    }

    //#end region

    //#region COLLECT PROBE DATA

    @Override
    protected void collectProbeDataAndFillTable() {
        // TODO Auto-generated method stub

    }

    //#end region

    //#end region

    //#region ACCESSORS

    //#region X LABEL

    /**
     * @return
     */
    public String getXLabel() {
        return xLabel.getValue();
    }

    /**
     * @param label
     */
    public void setXLabel(String label) {
        xLabel.setValue(label);
    }

    //#end region

    //#region X RANGE

    /**
     * @return
     */
    public String getXRange() {
        return xRange.getValue();
    }

    /**
     * @param range
     */
    public void setXRange(String range) {
        xRange.setValue(range);
    }

    //#end region

    //#region Y LABEL

    /**
     * @return
     */
    public String getYLabel() {
        return yLabel.getValue();
    }

    /**
     * @param label
     */
    public void setYLabel(String label) {
        yLabel.setValue(label);
    }

    //#end region

    //#region FIRST FAMILY LABEL

    /**
     * @return
     */
    public String getFirstFamilyLabel() {
        return firstFamilyLabel.getValue();
    }

    /**
     * @param label
     */
    public void setFirstFamilyLabel(String label) {
        firstFamilyLabel.setValue(label);
    }

    //#end region

    //#region FIRST FAMILY RANGE

    /**
     * @return
     */
    public String getFirstFamilyRange() {
        return firstFamilyRange.getValue();
    }

    /**
     * @param range
     */
    public void setFirstFamilyRange(String range) {
        firstFamilyRange.setValue(range);
    }

    //#end region

    //#region SECOND FAMILY LABEL

    /**
     * @return
     */
    public String getSecondFamilyLabel() {
        return secondFamilyLabel.getValue();
    }

    /**
     * @param label
     */
    public void setSecondFamilyLabel(String label) {
        secondFamilyLabel.setValue(label);
    }

    //#end region

    //#region SECOND  FAMILY RANGE

    /**
     * @return
     */
    public String getSecondFamilyRange() {
        return secondFamilyRange.getValue();
    }

    /**
     * @param range
     */
    public void setSecondFamilyRange(String range) {
        secondFamilyRange.setValue(range);
    }

    //#end region

    //#region PROBE

    /**
     * @return
     */
    public String getProbeName() {
        return probeName.getValue();
    }

    /**
     * @param name
     */
    public void setProbeName(String name) {
        probeName.setValue(name);
    }

    //#end region

    //#region SWEEP OUTPUT MODEL

    /**
     * @return
     */
    public String getSweepOutputModelName() {
        return sweepOutputModel.getValue();
    }

    /**
     * @param sweepOutputModel
     */
    public void setSweepOutputModelName(String sweepOutputModel) {
        this.sweepOutputModel.setValue(sweepOutputModel);
    }

    //#end region

    //#region PROBE TABLE

    /**
     * @return
     */
    public String getFirstProbeTable() {
        return firstProbeTable.getValue();
    }

    /**
     * @param firstProbeTable
     */
    public void setFirstProbeTable(String firstProbeTable) {
        this.firstProbeTable.setValue(firstProbeTable);
    }

    //#end region

    //#region COLUMN INDEX

    /**
     * @return
     */
    public String getProbeColumnIndex() {
        return probeColumnIndex.getValue();
    }

    /**
     * @param index
     */
    public void setProbeColumnIndex(String index) {
        probeColumnIndex.setValue(index);
    }

    //#end region

    //#region ROW INDEX

    /**
     * @return
     */
    public String getProbeRowIndex() {
        return probeRowIndex.getValue();
    }

    /**
     * @param index
     */
    public void setProbeRowIndex(String index) {
        probeRowIndex.setValue(index);
    }

    //#end region

    //#end region

}

最佳答案

对于选项E,通过使用“ final”修饰符,可以防止换入全新的AttributeAtom,同时仍然允许获取/设置:

public final AttributeAtom<String> myAttribute = new FilePathAttributeAtom("myAttribtue");


然后将允许以下内容:

myAtom.myAttribute.get();
myAtom.myAttribute.set(newValue)


但是您担心的事情不会是:

myAtom.myAttribute = completelyDifferentAttribute

关于java - Java中的“特殊属性/属性”而不是getter/setter,以避免样板代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31357420/

相关文章:

php - 常量表达式包含无效操作

java - Spring jpa 异常 - 发现对集合的共享引用

c# - 通过搜索特定的泛型接口(interface)参数获取实现泛型接口(interface)的类型

java - 在重写的父类(super class)方法中访问子类属性。可能的?

java - 如何从使用反射实现接口(interface)的类的字段中获取值?

java在不创建新实例的情况下遍历数组字段

ios - Swift 类 : Property not initialized at super. init 调用中的错误 - 如何初始化需要在其初始化参数中使用 self 的属性

java - 如何从java中的命令行参数或stdin重定向读取?

java - 活锁还是死锁?

Java:将UTF8字符串转换为另一种编码的字节数组