DEV Community

SameX
SameX

Posted on

HarmonyOS Next type conversion deep analysis: safe adaptation from basic data to objects

In HarmonyOS Next development, type conversion is a key link in realizing polymorphic programming and data interaction.Cangjie Language ensures the security and controllability of type conversion through a strict type system, combining the is and as operators and explicit conversion syntax.This article is based on the "Cangjie Programming Language Development Guide" and analyzes the type conversion rules and best practices in different scenarios.

1. Basic data type conversion: explicit operations and overflow processing

Cangjie language does not support implicit type conversion, and all basic data type conversions need to be performed explicitly to avoid runtime accidents.

1. Numerical type conversion

Use the target type (expression) syntax to support conversion between integers and floating-point numbers:

let intValue: Int32 = 100
let uintValue: UInt32 = UInt32(intValue) // 100
let floatValue: Float32 = Float32(intValue) // 100.0

// Overflow processing: Overflow errors that can be detected during the compilation period
let overflow: Int8 = Int8(130) // Compilation error: 130 exceeds the Int8 range (-128~127)
Enter fullscreen mode Exit fullscreen mode

2. Conversion between Rune and integers

  • Rune to UInt32: Get Unicode scalar value
  let char: Rune = 'π'
  let code: UInt32 = UInt32(char) // 960
Enter fullscreen mode Exit fullscreen mode
  • Integer to Rune: Make sure the value is within the valid range of Unicode
  let validCode: Int = 0x41 // 'A'
let char: Rune = Rune(validCode) // Success
let invalidCode: Int = 0x200000 // Out of range, throw exception during runtime
Enter fullscreen mode Exit fullscreen mode

2. Object type conversion: safe adaptation under polymorphic model

Object type conversion depends on subtype relationships, and is implemented through the is (type check) and as (safe conversion) operators.

1. is operator: runtime type judgment

Returns a Boolean value to determine whether the object is of a certain type or its subtype:

open class Animal {}
class Dog <: Animal {}

let pet: Animal = Dog()
println(pet is Dog) // true: Dog is a subclass of Animal
println(pet is Animal) // true: judging one's own type
Enter fullscreen mode Exit fullscreen mode

2. as operator: safe conversion and cast conversion

  • Safe conversion: Returns the Option type, None when it fails
  let animal: Animal = Dog()
if let dog = animal as? Dog { // Safe conversion
println("Conversion is successful, it is a Dog instance")
  } else {
println("Conversion failed")
  }
Enter fullscreen mode Exit fullscreen mode
  • Captive: Use as!, if it fails, it crashes at runtime (must make sure the type is correct)
let dog: Dog = animal as! Dog // cast, if animal is not Dog instance, it will crash
Enter fullscreen mode Exit fullscreen mode

3. Interface and class conversion rules

  • Class to Interface: Class instances that implement interfaces can be implicitly converted to interface types (upward transformation)
  interface Flyable {}
  class Bird <: Flyable {}
  let bird: Bird = Bird()
let flyable: Flyable = bird // Legal, Bird implements the Flyable interface
Enter fullscreen mode Exit fullscreen mode
  • Interface to class: It needs to be explicitly converted through as and succeeded only if the instance is actually type of this class
  let obj: Flyable = Bird()
if let bird = obj as? Bird { // Conversion is successful
// Access Bird-specific members
  }
Enter fullscreen mode Exit fullscreen mode

3. Type conversion between cross-package and generic scenes

1. Limitations of cross-packet type conversion

If the interface or class is defined in other packages, you need to pay attention to the impact of the sealed modifier:

// Define sealed interface in package A
package A
sealed interface InternalService {}

// Try to implement it in package B
package B
import A.*
class ServiceImpl <: InternalService {} // Compilation error: sealed interface is only implemented in package A
Enter fullscreen mode Exit fullscreen mode

2. Type constraints in generic functions

Restrict the effectiveness of type conversion by generic constraints, for example, only types that implement specific interfaces can participate in conversion:

func processData<T: Loggable>(data: T) {
if data is Serializable { // Require T to implement both Loggable and Serializable
        let serializable = data as? Serializable
// Handle serialization logic
    }
}
Enter fullscreen mode Exit fullscreen mode

3. Subtype conversion of tuples and function types

  • Tuple Type: When each element type is a subtype, the tuple is a subtype
  let point: (Int, Int) = (1, 2)
let superPoint: (Number, Number) = point // Assume Int is a subtype of Number (Sample Scenario)
Enter fullscreen mode Exit fullscreen mode
  • Function type: When the parameter type is parent type, the return type is child type, the function is child type
  func superFunc(x: Number) -> Animal { /* ... */ }
  func subFunc(x: Int) -> Dog { /* ... */ }
let funcVar: (Number) -> Animal = subFunc // Legal, subtype functions can be assigned to parent-type function variables
Enter fullscreen mode Exit fullscreen mode

4. Typical traps and evasion strategies for type conversion

1. Runtime errors caused by type erasing

Generic containers may lose specific type information, resulting in the as conversion failure:

let list: Array<Any> = [Dog()]
let dog = list[0] as? Dog // Successful, runtime type is Dog
let wrongType = list[0] as? Cat // Failed, return None
Enter fullscreen mode Exit fullscreen mode

2. Avoid overuse of casts

Cases (as!) destroy type safety and should be avoided by design:

// Counterexample: Relying on casts, there is a risk of crash
let obj: Any = "text"
let num = obj as! Int // crashes during runtime

// Positive example: check the type first and then convert it
if let str = obj as? String {
// Handle string logic
}
Enter fullscreen mode Exit fullscreen mode

3. Integrity check of interface implementation

If the class does not fully implement the interface, the type conversion may fail implicitly:

interface TwoFunctions {
    func f1()
    func f2()
}
class PartialImpl <: TwoFunctions {
public func f1() {} // f2 is not implemented, compilation error
}
Enter fullscreen mode Exit fullscreen mode

5. Practical scenario: Type conversion design of equipment adaptation layer

Scenario: Build a cross-device adaptation layer and uniformly process data formats of devices of different brands

1. Define unified interface and specific device classes

// Unified data interface
interface DeviceData {
    func toJSON(): String
}

// Data format of device A (class)
class DeviceAData <: DeviceData {
    private let value: Int
    public func toJSON(): String {
        "{\"value\": \(value)}"
    }
}

// Data format (structure) of device B
struct DeviceBData <: DeviceData {
    let code: String
    public func toJSON(): String {
        "{\"code\": \"\(code)\"}"
    }
}
Enter fullscreen mode Exit fullscreen mode

2. Type conversion and polymorphic processing of adapter layer

func processDeviceData(data: Any) {
if let deviceData = data as? DeviceData { // Convert to unified interface
        let json = deviceData.toJSON()
        sendToCloud(json)
    } else {
println("Unsupported data type")
    }
}

// Call example
let dataA = DeviceAData(value: 100)
let dataB = DeviceBData(code: "DEV_B")
processDeviceData(data: dataA) // Successfully converted and processed
processDeviceData(data: "invalid") // Output unsupported data types
Enter fullscreen mode Exit fullscreen mode

3. Generic Optimization: Constrain input types

func safeProcessData<T: DeviceData>(data: T) {
let json = data.toJSON() // Directly call the interface method without conversion
// Other processing logic
}
Enter fullscreen mode Exit fullscreen mode

6. Summary: The safe way to type conversion

The type conversion system of HarmonyOS Next embodies the design concept of "explicit priority, safe and controllable":

  • Basic type: Avoid implicit errors through explicit conversion, and overflow processing ensures stability;
  • Object type: Relying on is/as to achieve safe polymorphic conversion, prohibiting unsafe implicit conversion;
  • Architecture Design: Reduce the dependence of runtime type conversion through interfaces and generic constraints and improve code maintainability.

Top comments (0)