简体   繁体   English

lambda 表达式中数组集的值保持为 null

[英]Value in array set in a lambda expression stays null

I'm using JDA (Java Discord API), a Java wrapper for Discord's public API. I'm trying to use a lambda expression to store my Discord bot's message ID in an array, then use the message ID outside of the lambda to add reactions to the bot's message.我正在使用 JDA(Java Discord API),Discord 的公共 API 的 Java 包装器。我正在尝试使用 lambda 表达式将我的 Discord 机器人的消息 ID 存储在数组中,然后使用消息 ID374 到 3580 之外对机器人消息的反应。 However, the value in the array stays null. Code:但是,数组中的值仍然是 null。代码:

String[] botMessageId = new String[1]; // Supposed to store the message ID of the bot's response to a command
event.getChannel().sendMessage("This is a message").queue(message -> {
        botMessageId[0] = message.getId(); // Bot sends a message to the channel and the array stores the message ID
});
event.getChannel().addReactionById(botMessageId[0], "\u1F1E6"); // Supposed to add a reaction to the message that the bot just sent
event.getChannel().addReactionById(botMessageId[0], "\u1F1E7");
event.getChannel().addReactionById(botMessageId[0], "\u1F1E8");

Error:错误:

java.lang.IllegalArgumentException: Message ID may not be null
    at net.dv8tion.jda.internal.utils.Checks.notNull(Checks.java:69)
    at net.dv8tion.jda.internal.utils.Checks.isSnowflake(Checks.java:39)
    at net.dv8tion.jda.api.entities.MessageChannel.addReactionById(MessageChannel.java:1714)
    at net.dv8tion.jda.internal.entities.TextChannelImpl.addReactionById(TextChannelImpl.java:482)
    at com.beta.cipherer.commands.Commands.onGuildMessageReceived(Commands.java:47)
    at net.dv8tion.jda.api.hooks.ListenerAdapter.onEvent(ListenerAdapter.java:466)
    at net.dv8tion.jda.api.hooks.InterfacedEventManager.handle(InterfacedEventManager.java:96)
    at net.dv8tion.jda.internal.hooks.EventManagerProxy.handleInternally(EventManagerProxy.java:88)
    at net.dv8tion.jda.internal.hooks.EventManagerProxy.handle(EventManagerProxy.java:70)
    at net.dv8tion.jda.internal.JDAImpl.handleEvent(JDAImpl.java:160)
    at net.dv8tion.jda.internal.handle.MessageCreateHandler.handleInternally(MessageCreateHandler.java:97)
    at net.dv8tion.jda.internal.handle.SocketHandler.handle(SocketHandler.java:36)
    at net.dv8tion.jda.internal.requests.WebSocketClient.onDispatch(WebSocketClient.java:952)
    at net.dv8tion.jda.internal.requests.WebSocketClient.onEvent(WebSocketClient.java:839)
    at net.dv8tion.jda.internal.requests.WebSocketClient.handleEvent(WebSocketClient.java:817)
    at net.dv8tion.jda.internal.requests.WebSocketClient.onBinaryMessage(WebSocketClient.java:990)
    at com.neovisionaries.ws.client.ListenerManager.callOnBinaryMessage(ListenerManager.java:385)
    at com.neovisionaries.ws.client.ReadingThread.callOnBinaryMessage(ReadingThread.java:276)
    at com.neovisionaries.ws.client.ReadingThread.handleBinaryFrame(ReadingThread.java:996)
    at com.neovisionaries.ws.client.ReadingThread.handleFrame(ReadingThread.java:755)
    at com.neovisionaries.ws.client.ReadingThread.main(ReadingThread.java:108)
    at com.neovisionaries.ws.client.ReadingThread.runMain(ReadingThread.java:64)
    at com.neovisionaries.ws.client.WebSocketThread.run(WebSocketThread.java:45)

When putting the methods for adding reactions into the lambda, I get this error:将添加反应的方法放入 lambda 时,出现此错误:

java.lang.IllegalArgumentException: ID may not be null
    at net.dv8tion.jda.internal.utils.Checks.notNull(Checks.java:69)
    at net.dv8tion.jda.internal.utils.Checks.notEmpty(Checks.java:75)
    at net.dv8tion.jda.api.utils.MiscUtil.parseSnowflake(MiscUtil.java:115)
    at net.dv8tion.jda.api.entities.MessageHistory.getMessageById(MessageHistory.java:322)
    at com.beta.cipherer.commands.StepCommands.onGuildMessageReceived(StepCommands.java:51)
    at net.dv8tion.jda.api.hooks.ListenerAdapter.onEvent(ListenerAdapter.java:466)
    at net.dv8tion.jda.api.hooks.InterfacedEventManager.handle(InterfacedEventManager.java:96)
    at net.dv8tion.jda.internal.hooks.EventManagerProxy.handleInternally(EventManagerProxy.java:88)
    at net.dv8tion.jda.internal.hooks.EventManagerProxy.handle(EventManagerProxy.java:70)
    at net.dv8tion.jda.internal.JDAImpl.handleEvent(JDAImpl.java:160)
    at net.dv8tion.jda.internal.handle.MessageCreateHandler.handleInternally(MessageCreateHandler.java:97)
    at net.dv8tion.jda.internal.handle.SocketHandler.handle(SocketHandler.java:36)
    at net.dv8tion.jda.internal.requests.WebSocketClient.onDispatch(WebSocketClient.java:952)
    at net.dv8tion.jda.internal.requests.WebSocketClient.onEvent(WebSocketClient.java:839)
    at net.dv8tion.jda.internal.requests.WebSocketClient.handleEvent(WebSocketClient.java:817)
    at net.dv8tion.jda.internal.requests.WebSocketClient.onBinaryMessage(WebSocketClient.java:990)
    at com.neovisionaries.ws.client.ListenerManager.callOnBinaryMessage(ListenerManager.java:385)
    at com.neovisionaries.ws.client.ReadingThread.callOnBinaryMessage(ReadingThread.java:276)
    at com.neovisionaries.ws.client.ReadingThread.handleBinaryFrame(ReadingThread.java:996)
    at com.neovisionaries.ws.client.ReadingThread.handleFrame(ReadingThread.java:755)
    at com.neovisionaries.ws.client.ReadingThread.main(ReadingThread.java:108)
    at com.neovisionaries.ws.client.ReadingThread.runMain(ReadingThread.java:64)
    at com.neovisionaries.ws.client.WebSocketThread.run(WebSocketThread.java:45)

Please let me know if there is a better way to achieve the result I need and why the value in the array is null.请让我知道是否有更好的方法来实现我需要的结果以及为什么数组中的值为 null。

Update: You have to wrap the emoji using Emoji.fromUnicode in JDA 5. So the new code is message.addReaction(Emoji.fromUnicode("U+1F1E6")) .更新:您必须在 JDA 5 中使用Emoji.fromUnicode包装表情符号。因此新代码为message.addReaction(Emoji.fromUnicode("U+1F1E6"))


You can directly add the reactions in the queue callback:您可以直接在队列回调中添加反应:

event.getChannel().sendMessage("This is a message").queue(message -> {
        message.addReaction("U+1F1E6").queue();
        message.addReaction("U+1F1E7").queue();
        message.addReaction("U+1F1E8").queue();
});

The Unicode escapes you are attempting to use, such as ἞8 , do not work in java. You need to use UTF-16 escape codes, which would be "?" instead.您尝试使用的 Unicode 转义符(例如἞8 )在 java 中不起作用。您需要使用 UTF-16 转义码,即"?" However, JDA also supports the codepoint notations, such as "U+XXXXX" , which will automatically be converted for you by addReaction .但是,JDA 也支持代码点表示法,例如"U+XXXXX" ,它会由addReaction自动为您转换。 In my example I used addReaction("U+1F1E8") , which converts the codepoint notation into the Unicode characters automatically.在我的示例中,我使用addReaction("U+1F1E8") ,它将代码点表示法自动转换为 Unicode 个字符。

Your code with the message array does not work, because queue callbacks are asynchronous.您的消息数组代码不起作用,因为queue回调是异步的。 This means they run at a later time, which means you need to put your code into the callback, rather than below it.这意味着它们会在稍后运行,这意味着您需要将代码放入回调中,而不是在其下方。

You're trying to dodge callback hell.你试图躲避回调地狱。 It doesn't work.它不起作用。

There is a reason you have to do the whole .queue(foo ->...) thing, and it's not 'just to annoy you'.你必须做整个.queue(foo ->...)事情是有原因的,这不是“只是为了惹恼你”。 Imagine it worked you like wanted (that the code runs immediately).想象一下它像你想要的那样工作(代码立即运行)。 Then obviously the API would just return that message ID.那么显然 API 只会返回该消息 ID。

You'd write this:你会这样写:

String botMessageId = 
event.getChannel().sendMessage("This is a message");

Much simpler.简单多了。 The reason it doesn't work that way is because that sendMessage thing is asynchronous .它不能那样工作的原因是因为 sendMessage 是异步的。 It won't run right away.它不会立即运行。 You're just asking the system: Hey, uh, when you get around to it, go send this thing.你只是在问系统:嘿,呃,当你有空时,go 发送这个东西。 On your own time, I'll just keep going while you take care of that.在你自己的时间,我会继续前进,而你会处理它。

The .queue() thing is an addendum: "... oh, and when you did finally get around to taking care of sending this thing, please do this immediately afterwards". .queue()是一个附录:“......哦,当你终于开始处理发送这个东西时,请在之后立即执行此操作”。

So, the code flow is:所以,代码流程是:

  • You ask the system to send a message, and then run the foo -> x code afterwards.您要求系统发送消息,然后运行foo -> x代码。
  • The system is still a long way off from doing that.该系统距离做到这一点还有很长的路要走。
  • You immediately keep going and call addReactionById .您立即继续并调用addReactionById You're passing a null pointer here, as the botMessageId[0] =... code hasn't run yet.您在此处传递了一个null指针,因为botMessageId[0] =...代码尚未运行。
  • That code crashes.该代码崩溃。
  • Sometime later the system sends the message and runs your botMessageId[0] =... code.稍后系统会发送消息并运行您的botMessageId[0] =...代码。 Yelling into the void, as nobody will ever look at this.对着虚空大喊大叫,因为没有人会看这个。 The only code that references botMessageId[0] has run and gone.引用botMessageId[0]的唯一代码已经运行并消失了。

The solution is to move all the stuff you do with the botMessageId value into the queue call itself :解决方案是将您使用botMessageId值执行的所有操作移至queue调用本身

event.getChannel().sendMessage("This is a message").queue(message -> {
  event.getChannel().addReactionById(message.getId(), "\u1F1E6");
  event.getChannel().addReactionById(message.getId(), "\u1F1E7");
  event.getChannel().addReactionById(message.getId(), "\u1F1E8");
});

Of course, if as part of responding to a message you'd want to send another message, then you'd have a .queue(msg -> {... }) inside another .queue(msg -> {... }) , so if this async model is used a lot, you end up with an endless cavalcade of -> , having to wrap all code that is of any interest into a lambda and passing it somewhere.当然,如果作为响应消息的一部分,你想发送一条消息,那么你将在另一个 .queue(msg -> .queue(msg -> {... }) .queue(msg -> {... }) ,所以如果这个异步 model 被大量使用,你最终会得到无穷无尽的->行列,必须将所有感兴趣的代码包装到 lambda 中并将它传递到某个地方。 Any attempt to debug this will be borderline impossible and the code is an unreadable mess.任何对此进行调试的尝试都将是不可能的,并且代码是一团不可读的混乱。 This is called 'callback hell'.这被称为“回调地狱”。

The correct solution in java land is to stop using async. java 地的正确解决方案是停止使用异步。 It is highly unlikely you need async solutions.不太可能需要异步解决方案。 Mdoern CPUs can easily juggle thousands of threads. Mdoern CPU 可以轻松兼顾数千个线程。 You can set their stack sizes quite low.您可以将它们的筹码量设置得非常低。

Of course, if this is API you did not write and have to use, that's not an alternative you can use here, unless the API also offers synchronous alternatives.当然,如果这是 API 你没有写但必须使用,那不是你可以在这里使用的替代方案,除非 API 也提供同步替代方案。

NB: The new String[1] trick is usually better written as AtomicRefererence<String> instead (slightly more idiomatic, AR has a few nice properties, and unlike arrays, has more sensible toString() and the like).注意: new String[1]技巧通常写成AtomicRefererence<String>更好(稍微更惯用,AR 有一些不错的属性,并且不像 arrays,有更明智的 toString() 等)。 You can use it as a hack to get around issues when the call is lambda based (with -> notation), but synchronous .当调用基于 lambda(使用->表示法)但同步时,您可以将其用作解决问题的技巧。 For example, someList.stream().forEach(foo -> bar) is synchronous: The lambda will run once for each item in the list on the spot, and then the forEach method itself returns, vs. what you have, where the .sendMessage method returns almost immediately, but the actual sending the message and running your lambda occurs much later, in another thread most likely.例如, someList.stream().forEach(foo -> bar)是同步的: lambda 将在现场为列表中的每个项目运行一次,然后forEach方法本身返回,而不是你所拥有的,其中.sendMessage方法几乎立即返回,但实际发送消息和运行 lambda 发生的时间要晚得多,很可能是在另一个线程中。 So, when synchronous lambdas are happening you can use this trick and it will work.因此,当发生同步 lambda 时,您可以使用这个技巧,它会起作用。 But if you're designing the API and this comes up, first consider rewriting it so lambdas aren't neccessary.但是,如果您正在设计 API 并且出现这种情况,请首先考虑重写它,这样 lambda 就不是必需的了。 You don't as a rule want to foist the burden of dealing with lack of exception/localvar/control-flow transparency on the callers of your API, after all.毕竟,您通常不想将处理缺乏异常/本地变量/控制流透明度的负担强加给 API 的调用者。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM