使用数据键

使用数据键访问和修改数据

一个数据访问器提供了通过 Key 访问和修改单个数据的方法。我们从一个示例开始:

代码示例:“治疗”一个数据访问器,如果有可能的话

import org.spongepowered.api.data.DataHolder;
import org.spongepowered.api.data.key.Keys;

public void heal(DataHolder target) {
    if (target.supports(Keys.HEALTH)) {
        double maxHealth = target.get(Keys.MAX_HEALTH).get();
        target.offer(Keys.HEALTH, maxHealth);
    }
}

现在是解释上面这个方法的时候了。

第一行检查这一数据访问器是否支持一个生命值。如果支持,那么就可以被“治疗”。因为一个数据访问器并没有可能在没有最大生命值的情况下拥有关于生命值的数据,反之亦然。使用 supports() 检查数据键是否存在足够了。

第二行使用 get() 方法试图获取数据访问器的最大生命值。除了 get() 方法, getOrNull()getOrElse() 同样通过在第一个参数传入 Key 获取数据。一般情况下应该使用 get() 方法获取一个表示数据的 Optional ,如果数据不存在则返回一个 Optional.empty() 。因为我们已经检查并确认了 Key 的存在,我们就可以直接使用 Optionalget() 方法,而不必进行更进一步的检查。我们也可以直接使用 getOrNull() ,它等价于 get(key).orNull() ,从而摆脱 Optional 。第三种方式是 getOrElse() ,在数据不存在时使用第二个参数提供的默认值代替。

第三行的代码把数据设置回了数据访问器。我们提供一个 Key 表示当前生命值,并提供之前获取到的最大生命值。因此我们就把这个数据访问器“治疗”成了最大生命值。存在一系列的 offer() 方法用于不同的设置需求,所有的 offer() 方法都返回一个 DataTransactionResult ,其包含了数据是否被成功设置。现在,我们将使用那个期望传入一个 Key 和一个对应值的方法,不过在下一页我们会遇到更多。既然我们已经知道了我们对于生命值的设置一定会成功(因为该数据访问器支持这一做法),我们就可以直接无视返回值。

当然,也可以使用 remove() 方法完全移除一个 DataHolder 的数据。只需要简单地把对应的 Key 传入就可以做到这一点。下面的示例演示了如何移除一个数据访问器对应的自定义名称:

public void removeName(DataHolder target) {
    target.remove(Keys.DISPLAY_NAME);
}

转化数据

除了获取、修改或者设置一个值,还有一种方式可以和数据进行交互。通过使用数据访问器的 transform() 方法,我们可以传入一个 Key 和一个 Function 。在内部,对应数据键的值会被获取并使用给定的 Function 作用于其上。返回值随即被设置回数据访问器,并相应地返回一个 DataTransactionResult

现在,作为示例,我们设置一个数据访问器的最大生命值为原先的两倍。

import java.util.function.Function;

public void buff(DataHolder target) {
    target.transform(Keys.MAX_HEALTH, new Function<Double,Double>() {
        @Override
        public Double apply(Double input) {
            return (input == null) ? 0 : input * 2;
        }
    });
}

当然,因为你在使用 Java 8,所以你可以直接使用 Lambda 表达式减少代码长度:

public void buff(DataHolder target) {
    target.transform(Keys.MAX_HEALTH, d -> (d == null) ? 0 : 2*d);
}

请注意,在两种情况下我们都需要确保我们传入的 Function 可以处理 null 。你还会注意到,这里根本没有检查当前数据访问器是否支持 Keys#MAX_HEALTH 这一数据键。如果当前数据访问器根本不支持这一数据键, transform() 便会执行失败,并返回一个对应的 DataTransactionResult

数据值

There are cases where you may care about not only the direct value for a Key, but the keyed value encapsulating it. In that case, use the getValue(key) method instead of get(key). You will receive an object inheriting from Value which contains a copy of the original value. As Keys#SPAWNABLE_ENTITIES is a WeightedCollectionValue, we can get a list of potentional entity that could spawn using the Value of the key.

代码示例:使目标濒临死亡

public void scare(DataHolder target) {
    if (target.supports(Keys.NEXT_ENTITY_TO_SPAWN)) {
        WeightedCollectionValue value = target.getValue(Keys.NEXT_ENTITY_TO_SPAWN).get();
        List<Entity> entities = value.get(new Random());
    }
}