使用 Mixin

注解

此页适用于 SpongeCommon、 SpongeForge 和 SpongeVanilla。这三个仓库利用 Mixin 挂钩到底层进行实现 (原版 Minecraft 服务器和 Forge 服)。

Mixins是一种在运行时修改Java代码的方式,它向类中添加额外的行为。这可以将我们需要的功能加入到原版的Minecraft中去。Mixins对于Sponge的运行来说是必须的。

Mixin Wiki 上对一些关于Mixin使用的核心概念进行了基本的介绍。

上面的Wiki相当基础,如果你是一位有经验的Java开发者,你可以跳过第4节,其中讨论了mixins本身。

如果你是刚开始写 Mixin,我们强烈建议将 SpongeCommon 代码仓库中所有的范例 全部仔细阅读一遍。这些范例覆盖了大多数复杂的使用情景,同时也带有详尽的文档。同时你也应当查阅 Mixin 仓库本身的 Javadoc,大多数 Mixin 的功能的文档都可以在那里找到。

注解

不仅是 Sponge,还有很多项目也在使用 Mixin。所以 Mixin 有其自己的文档。

Mixin 与内部类

尽管可以使用 lambda 表达式,你不能在 Mixin 的(匿名)内部类中使用非静态的(匿名)内部类。

这意味着像后面的一些代码会搞坏mixin,同时也会把Sponge搞到万劫不复的境地。

return new Collection<ItemStack>() {
    @Override
    public Iterator<ItemStack> iterator() {
        return new Iterator<ItemStack>() {
            // THIS WILL NOT WORK!

            @Override
            public boolean hasNext() {
                // Code skipped
            }

            @Override
            public ItemStack next() {
                // Code skipped
            }
        };
    }

    // Other methods skipped
};

这一规则适用于所有带有 @Mixin 注解的类。不经过 Mixin 处理的类完全可以使用这些语言特性。有两种规避此规则的方法。

一种方法是使用单独的工具类,因为工具类并不会被 Mixin 处理然后合并入目标类中。因此,下面的代码可以正常工作。

public class SampleCollection implements Collection<ItemStack> {

    private final TargetClass target;

    public SampleCollection(TargetClass target) {
        this.target = target;
    }

    @Override
    public Iterator<ItemStack> iterator() {
        return new Iterator<ItemStack>() {

            @Override
            public boolean hasNext() {
                // Code skipped
            }

            @Override
            public ItemStack next() {
                // Code skipped
            }
        };
    }

    // Other methods skipped
}

@Mixin(TargetClass.class)
public abstract class SomeMixin {
    public Collection<ItemStack> someFunction() {
        return new SampleCollection((TargetClass) (Object) this);
    }
}

另一种方法是将所有内部类直接写为 Mixin 类的内部类或者某个方法的局部类,而非互相嵌套。例如:

@Mixin(TargetClass.class)
public abstract class SomeMixin {

    private final class SampleIterator implements Iterator<ItemStack> {

        @Override
        public boolean hasNext() {
            // Code skipped
        }

        @Override
        public ItemStack next() {
            // Code skipped
        }
    }

    public Collection<ItemStack> someFunction() {
        return new Collection<ItemStack>() {
            @Override
            public Iterator<ItemStack> iterator() {
                return new SampleIterator();
            }

            // Other methods skipped
        };
    }
}