如果我创建一个数组,然后填充它,Kotlin认为数组中可能有空值,并迫使我考虑到这个
val strings = arrayOfNulls<String>(10000)
strings.fill("hello")
val upper = strings.map { it!!.toUpperCase() } // requires it!!
val lower = upper.map { it.toLowerCase() } // doesn't require !!
创建填充数组没有此问题
val strings = Array(10000, {"string"})
val upper = strings.map { it.toUpperCase() } // doesn't require !!
如何告诉编译器strings.fill("hello")
的结果是NonNull数组?
答案 0 :(得分:2)
经验法则:如果有疑问,请明确指定类型(对此有一种特殊的重构):
val strings1: Array<String?> = arrayOfNulls<String>(10000)
val strings2: Array<String> = Array(10000, {"string"})
因此,您看到strings1
包含可以为空的项目,而strings2
则不包含。那只是决定如何使用这些数组:
// You can simply use nullability in you code:
strings2[0] = strings1[0]?.toUpperCase ?: "KOTLIN"
//Or you can ALWAYS cast the type, if you are confident:
val casted = strings1 as Array<String>
//But to be sure I'd transform the items of the array:
val asserted = strings1.map{it!!}
val defaults = strings1.map{it ?: "DEFAULT"}
答案 1 :(得分:1)
没有办法告诉编译器。变量的类型在声明时确定。在这种情况下,变量被声明为可以包含空值的数组。
fill()方法不声明新变量,它只修改现有变量的内容,因此不会导致变量类型发生变化。
答案 2 :(得分:1)
为什么填充数组工作正常
填充数组在用作第二个参数的lambda调用期间推断出数组的类型:
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
public class Test {
private JFrame frame;
private JTextField textField;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Test window = new Test();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public Test() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
JCheckBox chA = new JCheckBox("A");
chA.setFont(new Font("Adobe Arabic", Font.PLAIN, 12));
frame.getContentPane().add(chA, gbc);
// chA.setEnabled(false);
JCheckBox chB = new JCheckBox("B");
chB.setFont(new Font("Adobe Arabic", Font.PLAIN, 12));
frame.getContentPane().add(chB, gbc);
// chB.setEnabled(false);
JCheckBox chckbx1 = new JCheckBox("C");
chckbx1.setFont(new Font("Adobe Arabic", Font.PLAIN, 12));
frame.getContentPane().add(chckbx1, gbc);
chckbx1.setEnabled(false);
JPanel panel = new JPanel();
frame.add(panel, gbc);
textField = new JTextField();
JScrollPane scrollPane = new JScrollPane(textField);
// scrollPane.add(textField);
textField.setColumns(6);
panel.add(scrollPane);
frame.pack();
frame.setLocationRelativeTo(null);
panel.setVisible(false);
ActionListener al = (ActionEvent e) -> {
if (chA.isSelected() || chB.isSelected()) {
chckbx1.setEnabled(true);
}
//setting the panel to visible when box a or B and chckbx1 has been selected
if ((chA.isSelected() || chB.isSelected()) && chckbx1.isSelected()) {
panel.setVisible(true);
frame.revalidate();
frame.repaint();
}
};
chA.addActionListener(al);
chB.addActionListener(al);
chckbx1.addActionListener(al);
}
}
生成<body>
<section id="firstpage" data-role="page">
<div data-role="header">
<h1>Activity</h1>
</div>
<div class="ui-content">
<p>This is the content on page 1</p>
<!-- put some kind of a table with cells here -->
</div>
<div data-role="footer">
<!-- Buttons go here -->
</div>
</section>
<section id="secondpage" data-role="page">
<div data-role="header">
<h1>label2</h1>
</div>
<div class="ui-content">
<p>This is the content on page 1</p>
<!-- put some kind of a table with cells here -->
</div>
<div data-role="footer">
<!-- Buttons go here -->
</div>
</section>
</body>
val strings = Array(10000, {"string"})
生成Array<String>
因此,将val strings = Array(10000, { it -> if (it % 2 == 0) "string" else null })
左侧的声明更改为与lambda不匹配的声明无效。如果存在冲突,则表示存在错误。
如何使arrayOfNulls正常工作
对于Array<String?>
问题,他们键入您指定的调用=
在函数签名中用作泛型类型arrayOfNulls
,函数arrayOfNulls<String>
返回{{1}这意味着可以为空。代码中没有任何内容更改该类型。 T
方法仅将值设置为现有数组。
要将此nullable-element数组转换为非可空元素列表,请使用:
arrayOfNulls
这很好,因为您的Array<T?>
调用无论如何转换为列表,所以为什么不事先转换。现在,根据数组的大小,这可能是高性能的,如果在CPU高速缓存中,副本可能很快。如果它很大并且没有表现,你可以使这个懒惰:
fill
或者您可以通过复制保留数组,但实际上这没有任何意义,因为您使用val nullableStrings = arrayOfNulls<String>(10000).apply { fill("hello") }
val strings = nullableStrings.filterNotNull()
val upper = strings.map { it.toUpperCase() } // no !! needed
撤消它:
map
在Java或Kotlin代码(JetBrains研究统计数据)中,数组确实不常见,除非代码进行了非常低级别的优化。使用列表可能会更好。
鉴于你最终可能会以列表结束,也许从那里开始并放弃阵列。
val nullableStrings = arrayOfNulls<String>(10000).apply { fill("hello") }
val strings = nullableStrings.asSequence().filterNotNull()
val upper = strings.map { it.toUpperCase() } // no !! needed
但是,如果你不能停止使用数组的任务,并且真的必须在没有副本的情况下施放...
您总是可以编写一个执行两项操作的函数:首先,检查所有值是否为null,如果是,则返回强制转换为非null的数组。这有点hacky,但只是因为差异是可空性的,所以是安全的。
首先,在map
上创建一个扩展函数:
val nullableStrings = arrayOfNulls<String>(10000).apply { fill("hello") }
val strings: Array<String> = Array(nullableStrings.size, { idx -> nullableStrings[idx]!! })
然后使用此函数new函数进行转换(元素检查为非null转换):
val nullableStrings = listOf("a","b",null,"c",null,"d")
val strings = nullableStrings.filterNotNull()
但即使谈到最后一个选项,我也觉得很脏。