TypeConverter 实现的索引越界错误
Posted
技术标签:
【中文标题】TypeConverter 实现的索引越界错误【英文标题】:Index out of bound error with TypeConverter implementation 【发布时间】:2021-05-17 23:43:46 【问题描述】:我正在尝试将这个数据类保存到数据库中:
@Entity
@Parcelize
data class Rocket(
@SerializedName("active")
val active: Boolean = false,
@SerializedName("boosters")
val boosters: Int = 0,
@SerializedName("company")
val company: String = "",
@SerializedName("cost_per_launch")
val costPerLaunch: Int = 0,
@SerializedName("country")
val country: String = "",
@SerializedName("description")
val description: String = "",
@SerializedName("diameter")
val diameter: Diameter = Diameter(),
@SerializedName("engines")
val engines: Engines = Engines(),
@SerializedName("first_flight")
val firstFlight: String = "",
@SerializedName("first_stage")
val firstStage: FirstStage = FirstStage(),
@SerializedName("flickr_images")
val flickrImages: List<String> = listOf(),
@SerializedName("height")
val height: Height = Height(),
@PrimaryKey
@SerializedName("id")
val id: String = "",
@SerializedName("landing_legs")
val landingLegs: LandingLegs = LandingLegs(),
@SerializedName("mass")
val mass: Mass = Mass(),
@SerializedName("name")
val name: String = "",
@SerializedName("payload_weights")
val payloadWeights: List<PayloadWeight> = listOf(),
@SerializedName("second_stage")
val secondStage: SecondStage = SecondStage(),
@SerializedName("stages")
val stages: Int = 0,
@SerializedName("success_rate_pct")
val successRatePct: Int = 0,
@SerializedName("type")
val type: String = "",
@SerializedName("wikipedia")
val wikipedia: String = ""
): Parcelable
@Parcelize
data class Diameter(
@SerializedName("feet")
val feet: Double = 0.0,
@SerializedName("meters")
val meters: Double = 0.0
): Parcelable
@Parcelize
data class Engines(
@SerializedName("engine_loss_max")
val engineLossMax: Int = 0,
@SerializedName("isp")
val isp: Isp = Isp(),
@SerializedName("layout")
val layout: String = "",
@SerializedName("number")
val number: Int = 0,
@SerializedName("propellant_1")
val propellant1: String = "",
@SerializedName("propellant_2")
val propellant2: String = "",
@SerializedName("thrust_sea_level")
val thrustSeaLevel: ThrustSeaLevel = ThrustSeaLevel(),
@SerializedName("thrust_to_weight")
val thrustToWeight: Double = 0.0,
@SerializedName("thrust_vacuum")
val thrustVacuum: ThrustVacuum = ThrustVacuum(),
@SerializedName("type")
val type: String = "",
@SerializedName("version")
val version: String = ""
): Parcelable
@Parcelize
data class FirstStage(
@SerializedName("burn_time_sec")
val burnTimeSec: Int = 0,
@SerializedName("engines")
val engines: Int = 0,
@SerializedName("fuel_amount_tons")
val fuelAmountTons: Double = 0.0,
@SerializedName("reusable")
val reusable: Boolean = false,
@SerializedName("thrust_sea_level")
val thrustSeaLevel: ThrustSeaLevel = ThrustSeaLevel(),
@SerializedName("thrust_vacuum")
val thrustVacuum: ThrustVacuum = ThrustVacuum()
): Parcelable
@Parcelize
data class Height(
@SerializedName("feet")
val feet: Double = 0.0,
@SerializedName("meters")
val meters: Double = 0.0
): Parcelable
fun toStringMetric(): String
return "Height: $meters m"
fun toStringImperial(): String
return "Height: $feet ft"
@Parcelize
data class LandingLegs(
@SerializedName("material")
val material: String = "",
@SerializedName("number")
val number: Int = 0
): Parcelable
@Parcelize
data class Mass(
@SerializedName("kg")
val kg: Int = 0,
@SerializedName("lb")
val lb: Int = 0
): Parcelable
fun toStringMetric(): String
return "Mass: $kg kg"
fun toStringImperial(): String
return "Mass: $lb lbs"
@Parcelize
data class PayloadWeight(
@SerializedName("id")
val id: String = "",
@SerializedName("kg")
val kg: Int = 0,
@SerializedName("lb")
val lb: Int = 0,
@SerializedName("name")
val name: String = ""
): Parcelable
@Parcelize
data class SecondStage(
@SerializedName("burn_time_sec")
val burnTimeSec: Int = 0,
@SerializedName("engines")
val engines: Int = 0,
@SerializedName("fuel_amount_tons")
val fuelAmountTons: Double = 0.0,
@SerializedName("payloads")
val payloads: Payloads = Payloads(),
@SerializedName("reusable")
val reusable: Boolean = false,
@SerializedName("thrust")
val thrust: Thrust = Thrust()
): Parcelable
@Parcelize
data class Isp(
@SerializedName("sea_level")
val seaLevel: Int = 0,
@SerializedName("vacuum")
val vacuum: Int = 0
): Parcelable
@Parcelize
data class ThrustSeaLevel(
@SerializedName("kN")
val kN: Int = 0,
@SerializedName("lbf")
val lbf: Int = 0
): Parcelable
@Parcelize
data class ThrustVacuum(
@SerializedName("kN")
val kN: Int = 0,
@SerializedName("lbf")
val lbf: Int = 0
): Parcelable
@Parcelize
data class Payloads(
@SerializedName("composite_fairing")
val compositeFairing: CompositeFairing = CompositeFairing(),
@SerializedName("option_1")
val option1: String = ""
): Parcelable
@Parcelize
data class Thrust(
@SerializedName("kN")
val kN: Int = 0,
@SerializedName("lbf")
val lbf: Int = 0
): Parcelable
@Parcelize
data class CompositeFairing(
@SerializedName("diameter")
val diameter: Diameter = Diameter(),
@SerializedName("height")
val height: Height = Height()
): Parcelable
In order to do that, I have to write type converters that convert non-primitive types. My naive solution is to just convert everything into a string that's divided by `;`.
这是我写的:
class RocketTypeConverter
@TypeConverter
fun fromDiameterToString(diam: Diameter?): String?
return diam?.let
"$diam.feet;$diam.meters"
@TypeConverter
fun fromStringToDiameter(str: String?): Diameter?
str?.let
str.split(";").also
return Diameter(
feet = it[0].toDouble(),
meters = it[1].toDouble()
)
return null
@TypeConverter
fun fromEnginesToString(engines: Engines?): String?
engines?.let
engines.apply
return "$engineLossMax" +
";" + fromIspToString(isp) +
";" + layout +
";" + "$number" +
";" + propellant1 +
";" + propellant2 +
";" + fromThrustSeaLevelToString(thrustSeaLevel) +
";" + "$thrustToWeight" +
";" + fromThrustVacuumToString(thrustVacuum) +
";" + type +
";" + version
return null
@TypeConverter
fun fromStringToEngines(str: String?): Engines?
str?.let
str.split(";").also
val isp = fromStringToIsp(it[1])
val thrustSeaLevel = fromStringToThrustSeaLevel(it[6])
val thrustVacuum = fromStringToThrustVacuum(it[8])
return if(isp != null && thrustSeaLevel != null && thrustVacuum != null)
Engines(
engineLossMax = it[0].toInt(),
isp = isp,
layout = it[2],
number = it[3].toInt(),
propellant1 = it[4],
propellant2 = it[5],
thrustSeaLevel = thrustSeaLevel,
thrustToWeight = it[7].toDouble(),
thrustVacuum = thrustVacuum,
type = it[9],
version = it[10]
)
else
null
return null
@TypeConverter
fun fromFirstStageToString(firstStage: FirstStage?): String?
firstStage?.let
firstStage.apply
return "$burnTimeSec" +
";" + "$engines" +
";" + "$fuelAmountTons" +
";" + (if(reusable) "1" else "0") +
";" + fromThrustSeaLevelToString(thrustSeaLevel) +
";" + fromThrustVacuumToString(thrustVacuum)
return null
@TypeConverter
fun fromStringToFirstStage(str: String?): FirstStage?
str?.let
str.split(";").also
val thrustSeaLevel = fromStringToThrustSeaLevel(it[4])
val thrustVacuum = fromStringToThrustVacuum(it[5])
return if(thrustSeaLevel != null && thrustVacuum != null)
FirstStage(
burnTimeSec = it[0].toInt(),
engines = it[1].toInt(),
fuelAmountTons = it[2].toDouble(),
reusable = it[3] == "1",
thrustSeaLevel = thrustSeaLevel,
thrustVacuum = thrustVacuum
)
else
null
return null
@TypeConverter
fun fromHeightToString(height: Height?): String?
height?.let
return "$height.feet;$height.meters"
return null
@TypeConverter
fun fromStringToHeight(str: String?): Height?
str?.let
str.split(";").also
return Height(
feet = it[0].toDouble(),
meters = it[1].toDouble()
)
return null
@TypeConverter
fun fromLadingLegsToString(landingLegs: LandingLegs?): String?
landingLegs?.let
return "$landingLegs.material;$landingLegs.number"
return null
@TypeConverter
fun fromStringToLandingLegs(str: String?): LandingLegs?
str?.let
str.split(";").also
return LandingLegs(
material = it[0],
number = it[1].toInt()
)
return null
@TypeConverter
fun fromMassToString(mass: Mass?): String?
mass?.let
return "$mass.kg;$mass.lb"
return null
@TypeConverter
fun fromStringToMass(str: String?): Mass?
str?.let
str.split(";").also
return Mass(
kg = it[0].toInt(),
lb = it[1].toInt()
)
return null
@TypeConverter
fun fromPayLoadWeightToString(payloadWeight: PayloadWeight?): String?
payloadWeight?.let
return "$payloadWeight.id;$payloadWeight.kg;$payloadWeight.lb;$payloadWeight.name"
return null
@TypeConverter
fun fromStringToPayloadWeight(str: String?): PayloadWeight?
str?.let
str.split(";").also
return PayloadWeight(
id = it[0],
kg = it[1].toInt(),
lb = it[2].toInt(),
name = it[3]
)
return null
@TypeConverter
fun fromSecondStageToString(secondStage: SecondStage?): String?
secondStage?.let
secondStage.apply
return "$burnTimeSec" +
";" + "$engines" +
";" + "$fuelAmountTons" +
";" + fromPayloadsToString(payloads) +
";" + (if(reusable) "1" else "0") +
";" + fromThrustToString(thrust)
return null
@TypeConverter
fun fromStringToSecondStage(str: String?): SecondStage?
str?.let
str.split(";").also
val payloads = fromStringToPayloads(it[3])
val thrust = fromStringToThrust(it[5])
return if(payloads != null && thrust != null)
return SecondStage(
burnTimeSec = it[0].toInt(),
engines = it[1].toInt(),
fuelAmountTons = it[2].toDouble(),
payloads = payloads,
reusable = it[4] == "1",
thrust = thrust
)
else
null
return null
@TypeConverter
fun fromThrustToString(thrust: Thrust?): String?
thrust?.let
return "$thrust.kN;$thrust.lbf"
return null
@TypeConverter
fun fromStringToThrust(str: String?): Thrust?
str?.let
str.split(";").also
return Thrust(
kN = it[0].toInt(),
lbf = it[1].toInt()
)
return null
@TypeConverter
fun fromPayloadsToString(payloads: Payloads?): String?
payloads?.let
return fromCompositeFairingToString(payloads.compositeFairing) + payloads.option1
return null
@TypeConverter
fun fromStringToPayloads(str: String?): Payloads?
str?.let
str.split(";").also
fromStringToCompositeFairing(it[0])?.let compositeFairing ->
return Payloads(
compositeFairing = compositeFairing,
option1 = it[1]
)
?: return null
return null
@TypeConverter
fun fromCompositeFairingToString(compositeFairing: CompositeFairing?): String?
compositeFairing?.let
return fromDiameterToString(compositeFairing.diameter) +
fromHeightToString(compositeFairing.height)
return null
@TypeConverter
fun fromStringToCompositeFairing(str: String?): CompositeFairing?
str?.let
str.split(";").also
val diameter = fromStringToDiameter(it[0])
val height = fromStringToHeight(it[1])
return if(diameter != null && height != null)
CompositeFairing(
diameter = diameter,
height = height
)
else
null
return null
@TypeConverter
fun fromIspToString(isp: Isp?): String?
isp?.let
return "$isp.seaLevel;$isp.vacuum"
return null
@TypeConverter
fun fromStringToIsp(str: String?): Isp?
str?.let
str.split(";").also
return Isp(
seaLevel = it[0].toInt(),
vacuum = it[1].toInt()
)
return null
@TypeConverter
fun fromThrustSeaLevelToString(thrustSeaLevel: ThrustSeaLevel?): String?
thrustSeaLevel?.let
return "$thrustSeaLevel.kN;$thrustSeaLevel.lbf"
return null
@TypeConverter
fun fromStringToThrustSeaLevel(str: String?): ThrustSeaLevel?
str?.let
str.split(";").also
return ThrustSeaLevel(
kN = str[0].toInt(),
lbf = str[1].toInt()
)
return null
@TypeConverter
fun fromThrustVacuumToString(thrustVacuum: ThrustVacuum?): String?
thrustVacuum?.let
return "$thrustVacuum.kN;$thrustVacuum.lbf"
return null
@TypeConverter
fun fromStringToThrustVacuum(str: String?): ThrustVacuum?
str?.let
str.split(";").also
return ThrustVacuum(
kN = str[0].toInt(),
lbf = str[1].toInt()
)
return null
@TypeConverter
fun fromFlickrImagesToString(flickrImages: List<String>?): String?
flickrImages?.let
var imagesString = ""
for(image in flickrImages)
imagesString += "$image;"
return imagesString.substring(0, (imagesString.length - 1))
return null
@TypeConverter
fun fromStringToFlickrImages(str: String?): List<String>?
str?.let
return str.split(";")
return null
@TypeConverter
fun fromPayloadWeightsToString(payloadWeights: List<PayloadWeight>?): String?
payloadWeights?.let
var weightsString = ""
for(weight in payloadWeights)
weightsString += fromPayLoadWeightToString(weight) + ";"
return weightsString.substring(0, (weightsString.length - 1))
return null
@TypeConverter
fun fromStringToPayloadWeights(str: String?): List<PayloadWeight>?
str?.let
val weights = mutableListOf<PayloadWeight>()
str.split(";").also
for(payloadWeight in it)
fromStringToPayloadWeight(payloadWeight)?.let payloadWeight ->
weights.add(payloadWeight)
?: return null
return weights
return null
我拥有它的方式是我有两个模块:launch
和 rocket
。每个都有自己的存储库,每个存储库都构建数据库:
private val dao: Database = Room.databaseBuilder(
context,
Database::class.java,
Database.NAME_DB
).build()
我遇到的问题是我收到此错误:java.lang.IndexOutOfBoundsException: Index: 1, Size: 1
。
现在,由于fromStringToIsp()
而发生错误。这看起来不像是算法错误,因为Isp
类只有两个字段,所以我不会在索引上犯错。如果我暴力破解fromStringToIsp()
通过,其他一些函数也会报越界错误。
那么我在实现 TypeConverters
时哪里出错了?
【问题讨论】:
【参考方案1】:您传递给fromStringToIsp()
的字符串不包含分号,因为在fromStringToEngines()
中您正在执行以下操作:
str.split(";").also
val isp = fromStringToIsp(it[1])
/* ... */
然后,在fromStringToIsp()
中,您可以:
str.split(";").also
return Isp(
seaLevel = it[0].toInt(),
vacuum = it[1].toInt()
)
当尝试访问it[1]
时,您会遇到异常,因为it
只有一个元素,即it[0]
。
【讨论】:
我知道索引越界异常是什么意思。我不明白为什么str
不包含分号。我使用return "$isp.seaLevel;$isp.vacuum"
从Isp
创建一个字符串。因此,如果数据库实现调用fromStringToType(String): Type
,那么fromTypeToString(Type): String
必须事先被调用。
再次阅读我的答案。您在两个级别上使用了相同的分隔符。以上是关于TypeConverter 实现的索引越界错误的主要内容,如果未能解决你的问题,请参考以下文章
TypeConverter() 在 Android 中的 Room 出现 TypeConverter 错误时具有私有访问权限