3

我定期使用 Eclipse 调试器,这一直困扰着我关于 IDE 的问题。步进过滤是一个非常宝贵的工具,这样我就不会进入没有源代码或者我根本不感兴趣的类。但是 Eclipse 在大多数情况下都不能正确处理。特别是,有“过滤简单的getter”和“过滤简单的setter”的选项。

我可能会使用一个 getter,它只是简单地返回成员变量的值。

private String value;
public String getValue()
{
    return value;
}

或者可能是一个懒惰地实例化昂贵对象的吸气剂。

private IObjectFactory instance;
public IObjectFactory getInstance()
{
    if (instance == null)
        instance = ObjectFactory.createFactory();
    return instance;
}

我可能会使用一个只设置成员变量值的设置器。

private String value;
public void setValue(String value)
{
    this.value = value;
}

我可能想支持流畅的语法。

private String value;
public ObjectFactory setValue(String value)
{
    this.value = value;
    return this;
}

或者也许做一些验证或事件触发。

private String user;
public void setUser(String user)
{
    if (StringUtils.isBlank(user))
        throw ExceptionHelper.argumentNull("user");
    this.user = user;
}

private String title;
public void setTitle(String title)
{
    if (!StringUtils.equals(this.title, title))
    {
        this.title= title;
        onPropertyChanged("title", title);
    }
}

对于这些用途中的每一种,使用 eclipse 进入代码会进入这些方法......

Eclipse 将什么视为“简单的 getter”或“简单的 setter”?

过滤器肯定是启用的:

步骤过滤设置

万一这很重要,我使用的是 Eclipse Kepler build 20130614-0229。我正在使用 JRE6 运行 Eclipse 和托管 Java 1.4 Web 应用程序的 Tomcat 7 服务器。尽管我们最终以 1.4 为目标,但它是使用 JDK6 在本地编译的,所以我认为这不是问题。我确实安装并使用了 JRebel,也许类加载器正在干扰确定什么被认为是“简单”的算法?结合启用的“逐步通过过滤器”选项,它可能会逐步执行我的代码。考虑到这一点后,我将进一步进行实验。

4

2 回答 2

2

好的,我想我找到了。

在正常情况下,如果启用了这些过滤器,则会跳过普通的 getter 和普通的 setter(问题中的示例 1 和 3)。如果安装并使用了一个特殊的类加载器,例如 JRebel,它会修改方法以连接到它们中,它似乎会干扰确定方法是“简单 getter”还是“简单 setter”的 Eclipse 算法。

所以在代码中可能看起来像这样的吸气剂:

public String getValue()
{
    return this.value;
}

从 JVM 的角度来看,可能会被更改为如下所示:

public String getValue()
{
    Proxy proxy = getProxy(this);
    return (String)proxy.invoke("getValue", new Object[] { });
    // this is all just an example,
    // it's defintely way more complicated than this
}

这段修改过的代码让 Eclipse 误以为“这不是一个简单的 getter,所以要一步到位”。确实如此,但实际的源代码是我实际的简单 getter,这让我感到困惑,“为什么 Eclipse 会进入这个简单的 getter?”

我进行了一个非常人为的测试,试图让步骤过滤起作用。

import org.apache.commons.lang.StringUtils;

public class Program
{
    public static void main(String[] args)
    {
        User bob = new User("0001", "Bob");
        String id = bob.getId(); //stepped over
        String name = bob.getName(); //stepped over
        IHome home = bob.getHome(); //stepped into

        bob.setId("foo"); //stepped into
        bob.setName("Bobby"); //stepped over
        String asString = bob.setNameFluent("Bobbo").toString(); //stepped into
        IHome newHome = Neighborhood.getHome("moo");
        bob.setHome(newHome); //stepped into
        return;
    }

    static class User
    {
        private String id;
        private String name;
        private IHome home;

        public User() { this("0001", null); }
        public User(String id, String name) { this.id = id; this.name = name; }

        public String getId() // simple
        {
            return id;
        }
        public String getName() // simple
        {
            return name;
        }
        public IHome getHome() // not simple
        {
            if (home == null)
                home = Neighborhood.getHome(id);
            return home;
        }

        public void setId(String id) // not simple
        {
            if (StringUtils.isBlank(id))
                throw ExceptionHelper.argumentBlank("id");
            this.id = id;
        }
        public void setName(String name) // simple
        {
            this.name = name;
        }
        public User setNameFluent(String name) // not simple
        {
            this.name = name;
            return this;
        }
        public void setHome(IHome home) // not simple
        {
            if (home != null)
            {
                this.home = home;
                onHomeChanged();
            }
        }

        protected void onHomeChanged()
        {
            this.id = home.getId();
        }
        public String toString()
        {
            return "User { name=" + getName() + ", home=" + getHome() + " }";
        }
    }

    static interface IHome
    {
        String getId();
        String getLocation();
    }

    static class Neighborhood
    {
        public static IHome getHome(String id)
        {
            return new Home(id);
        }

        static class Home implements IHome
        {
            private String id;
            public Home(String id) { this.id = id; }
            public String getId() { return id; }
            public String getLocation() { return "Home" + id; }
            public String toString() { return "Home: " + getLocation(); }
        }
    }

    static class ExceptionHelper
    {
        public static IllegalArgumentException argumentBlank(String name)
        {
            return new IllegalArgumentException("Argument " + name + " must not be blank");
        }
    }
}

使用默认配置(没有 JRebel 的 JDK6),步骤过滤似乎可以工作。试图踏入简单的方法实际上越过了它们。启用 JRebel 并再次单步执行代码后,它会单步执行所有方法。是否启用“逐步过滤器”并不重要。

tldr;

通过使用 JRebel,它确实使 Eclipse 感到困惑,使简单的 getter 和简单的 setter 看起来比原来更复杂。禁用 JRebel 将导致过滤器按预期工作。

于 2013-08-23T17:16:39.057 回答
1

是 Eclipse 网站上的一个页面,其中更详细地描述了这两个选项

过滤简单的吸气剂:

此选项控制在单步执行时是否应始终过滤简单的 Java bean 样式的 getter

过滤简单的设置器:

此选项控制在单步执行时是否应始终过滤简单的 Java bean 样式设置器

从它的声音来看,您给出的示例 1 和 3似乎确实是它们的意思不知道为什么你会看到你的行为。

编辑:看起来原始海报发现了问题;关于 JRebel 使方法过于复杂的一些事情。

于 2013-08-22T23:49:47.500 回答