Kotlin的String.capitalize()函数已被弃用,是否有更短的替代方式?

90
Kotlin已经弃用了String类上的 capitalize 函数,并建议使用的替代函数名称过长。这是一个他们在废弃该函数时做出正确决定但对用户体验却做出错误决策的例子。
例如,以下代码:
val x = listOf("foo", "bar", "baz").map { it.capitalize() }

通过IDE进行“清理”后,变成:

val x = listOf("foo", "bar", "baz").map { it.replaceFirstChar {
                    if (it.isLowerCase()) it.titlecase(
                        Locale.getDefault()
                    ) else it.toString()
                } }

这看起来相当丑陋。我们能做些什么来改善它吗?

8个回答

80

建议的替代方案很丑,因为它需要与 capitalize() 的行为等价:

  1. 依赖于默认语言环境
  2. 不会将首字母大写转换为标题大小写(例如,capitalize 不会将前导字符 'DŽ' 转换为 'Dž' —— 这里都是单个字符,请尝试选择它们)

如果你不太关心这种行为,可以使用更简单的表达式,使用固定的语言环境,并始终将第一个字符转换为标题大小写即使是大写的情况:

val x = listOf("foo", "bar", "baz").map { it.replaceFirstChar(Char::titlecase) }
这意味着,如果第一个字符是大写字母,例如 'DŽ',它将被转换为标题大小写变体 'Dž',而原始代码不会改动它。这实际上可能是期望的结果。 capitalize() 方法被弃用的原因之一是其行为不明确。例如:
- 行为 #2 相当奇怪 - 不将句子中的单词大写可能出人意料(C# 会对每个以空格分隔的单词进行标题大小写转换) - 不将单词的其他字符小写可能也出人意料
如果您想故意保留当前的确切行为,但使其更方便使用,您总可以编写自己的扩展函数,并给它一个适合您的名称(“capitalize(d)”可能不足以向不知情的读者提供足够的信息)。
fun String.titlecaseFirstCharIfItIsLowercase() = replaceFirstChar { 
    if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() 
}

或者对于不变语言环境版本,将大写字符的标题大小写:

fun String.titlecaseFirstChar() = replaceFirstChar(Char::titlecase)

1
map { it.replaceFirstChar(Char::uppercase) } 可能稍微简洁一些? - Adam Millerchip
@AdamMillerchip 很好的观点,这是避免“it”变量重名的更简洁的方法。我已经在我的答案中添加了它。 - Joffrey
1
请注意:建议替换中的标题部分是有原因的。例如,在标题 case 中,字符 dž 的替换为 Dž 而不是大写的DŽ。请直接返回翻译后的文本。 - Cristan
是的,我澄清了我的答案以解释你到底放弃了什么。 - Joffrey
这个 if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() 实际上是在 capitalize() 的弃用注释中建议的,但仍不是正确的解决方案。想象一下这个简单的例子 BAku Kotlin 的建议会保持原样。然而,大写意味着将单词的“第一个字符”大写,其余小写。capitalize() 是一个丑陋的解决方案,他们的新建议又是另一个丑陋的建议。说实话,如果他们没有提出一个误导性的解决方案,那么什么都不建议会更好。 - Farid
显示剩余3条评论

38
一个简洁的解决方案是在String上定义一个新的扩展函数,该函数使用更清晰的名称隐藏了令人讨厌的细节:
/**
 * Replacement for Kotlin's deprecated `capitalize()` function.
 */
fun String.capitalized(): String {
    return this.replaceFirstChar { 
        if (it.isLowerCase())
            it.titlecase(Locale.getDefault())
        else it.toString() 
    }
}

现在,您的旧代码可以看起来像这样:

val x = listOf("foo", "bar", "baz").map { it.capitalized() }

你需要在可以轻松导入的某个包的顶层定义扩展函数。例如,如果你有一个名为my.package.KotlinUtilsKotlinUtils.kt)的 Kotlin 文件,并像下面这样将定义放在其中:

package my.package

fun String.capitalized(): String {...}

然后,您可以使用以下方法在其他软件包中导入它:

import my.package.capitalized

我可以看出正确答案和这个答案之间有很大的区别。对于任何寻找Kotlin纯函数正确适用的人来说,这一定是推荐的答案。对于只需要转换少量字符串的情况,map函数是过度的,会增加额外的性能开销。 - Jack
1
或者只需使用“fun String.capitalized(): String = this.replaceFirstChar { it.titlecase() }”即可。在单个字符上调用“titlecase”是...嗯...一个有趣的概念。同样,在没有理由的情况下调用字符上的“toString”也是如此。 - Lutosław
@Lutosław 这就是 titlecase() 函数的设计方式(它在 Char 中定义):单个字符可以用多个字符大写,因此 Char.titlecase() 返回一个 String。如果您在执行任何字符串转换之前使用此 if 检查小写字母,则需要一个 else 分支返回一个 String,因此... 您需要 toString()。这不是没有原因的。 - Joffrey

7
val fruits = listOf("baNana", "avocAdo", "apPle", "kiwifRuit")
fruits
    .filter { it.startsWith("a") }
    .sortedBy { it }
    .map { it.lowercase().replaceFirstChar(Char::uppercase) }
    .forEach { println(it) }

输出:

Apple
Avocado

6

您可以在原始字符串上调用replaceFirstChar函数,并将变换函数作为输入传递。 变换函数接受第一个字符并使用uppercase()函数将其转换为大写字符。

val list = listOf("foo", "bar", "baz") .map {
     it.replaceFirstChar { firstChar ->
          firstChar.uppercase()
     }
 }
println("List - > $list")

输出

List - > [Foo, Bar, Baz]

1
这个怎么样?
fun main() {
    val x = listOf("foo", "bar", "baz").map { it[0].uppercase() + it.drop(1) }
    println(x)
}

输出:

[Foo, Bar, Baz]

请注意,如果存在空字符串,则此代码将失败。在这种情况下,Joffrey的答案更好。 - Adam Millerchip
说实话,我更喜欢使用 replaceFirstChar,不仅因为它处理空字符串的方式,而且也因为它更能反映出意图。 - Joffrey
我的解决方案使用了IntelliJ提出的完全相同的代码,我假设他们仔细评估过以确保它具有与弃用的capitalize()函数相同的语义。 - Stevey
@stevey。太好了。但是你要求一个更短的替代方案,这就是它。 - Adam Millerchip
@AdamMillerchip 你抓住我了。 - Stevey

1

我发现了一种方法,可以将来自API的字符串大写化,它似乎有效,在Kotlin文档中找到了它:

println("kotlin".replaceFirstChar { it.uppercase() }) // Kotlin

然后在我的代码中像这样使用它:

 binding.textDescriptions.text = "${it.Year} - ${it.Type.replaceFirstChar {it.uppercase()}}"

0

如果您不确定(也许您从 API 接收字符串)第一个字母是大写还是小写,您可以使用以下方法;

var title = "myTitle"
title.replaceFirstChar {
        if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else 
        it.toString()
    }

新标题将为"MyTitle"


0
你可以使用这个扩展函数来将字符串的第一个字符大写。
fun String.capitalize(): String {
        return this.replaceFirstChar {
            if (it.isLowerCase()) it.titlecase(Locale.getDefault())
            else it.toString()
        }
}

并像这样调用该方法

"abcd".capitalize()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接