This article is shared from Huawei Cloud Community "Multi-language programming returns multiple different types of method examples", author: Zhang Jian.
background
You may encounter methods that need to return multiple different types in some scenarios. For example, when protocol parsing reads a message, or more specifically when kubernetes starts parsing Yaml, how does it know whether this type belongs to Deployment or Service?
C
C language usually implements this function by using Struct (structure) and Union (union), as shown in the following example
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef enum {
MONKEY,
COW,
UNKNOWN
} AnimalType;
typedef struct {
char* description;
} Monkey;
typedef struct {
char* description;
} Cow;
typedef struct {
AnimalType type;
union {
Monkey monkey;
Cow cow;
};
} Animal;
Animal createAnimal(const char* animalType) {
Animal animal;
if (strcmp(animalType, "Monkey") == 0) {
animal.type = MONKEY;
animal.monkey.description = "I am a monkey!";
} else if (strcmp(animalType, "Cow") == 0) {
animal.type = COW;
animal.cow.description = "I am a cow!";
} else {
animal.type = UNKNOWN;
}
return animal;
}
int main() {
Animal animal1 = createAnimal("Monkey");
if (animal1.type == MONKEY) {
printf("%s\n", animal1.monkey.description);
}
Animal animal2 = createAnimal("Cow");
if (animal2.type == COW) {
printf("%s\n", animal2.cow.description);
}
Animal animal3 = createAnimal("Dog");
if (animal3.type == UNKNOWN) {
printf("Unknown animal type\n");
}
return 0;
}
C++
In C++, we can use base class pointers to point to objects of derived classes. You can use dynamic type identification (RTTI) to determine the type of an object at runtime
#include <iostream>
#include <stdexcept>
class Animal {
public:
virtual std::string toString() const = 0;
};
class Monkey : public Animal {
public:
std::string toString() const override {
return "I am a monkey!";
}
};
class Cow : public Animal {
public:
std::string toString() const override {
return "I am a cow!";
}
};
Animal* createAnimal(const std::string& animalType) {
if (animalType == "Monkey") {
return new Monkey();
}
if (animalType == "Cow") {
return new Cow();
}
throw std::runtime_error("Unknown animal type: " + animalType);
}
int main() {
try {
Animal* animal1 = createAnimal("Monkey");
if (Monkey* monkey = dynamic_cast<Monkey*>(animal1)) {
std::cout << monkey->toString() << std::endl;
}
delete animal1;
Animal* animal2 = createAnimal("Cow");
if (Cow* cow = dynamic_cast<Cow*>(animal2)) {
std::cout << cow->toString() << std::endl;
}
delete animal2;
}
catch (const std::runtime_error& e) {
std::cerr << e.what() << std::endl;
}
return 0;
}
Go
The common way of processing in Go is to return an interface or **interface{}** type. The caller uses Go language type assertions to check the specific type
package main
import (
"fmt"
)
type Animal interface {
String() string
}
type Monkey struct{}
func (m Monkey) String() string {
return "I am a monkey!"
}
type Cow struct{}
func (c Cow) String() string {
return "I am a cow!"
}
func createAnimal(typeName string) (Animal, error) {
switch typeName {
case "Monkey":
return Monkey{}, nil
case "Cow":
return Cow{}, nil
default:
return nil, fmt.Errorf("Unknown animal type: %s", typeName)
}
}
func main() {
animal1, err := createAnimal("Monkey")
if err != nil {
fmt.Println(err)
return
}
if monkey, ok := animal1.(Monkey); ok {
fmt.Println(monkey)
}
animal2, err := createAnimal("Cow")
if err != nil {
fmt.Println(err)
return
}
if cow, ok := animal2.(Cow); ok {
fmt.Println(cow)
}
}
Java
A common processing method in the Java language is to return the Object type or a basic type. Then the caller makes the instance of judgment. Or after Java17, you can use pattern matching to simplify the transformation
public class MultiTypeReturnExample {
static class Monkey {
@Override
public String toString() {
return "I am a monkey!";
}
}
static class Cow {
@Override
public String toString() {
return "I am a cow!";
}
}
public static Object createAnimal(String type) throws IllegalArgumentException {
switch (type) {
case "Monkey":
return new Monkey();
case "Cow":
return new Cow();
default:
throw new IllegalArgumentException("Unknown animal type: " + type);
}
}
public static void main(String[] args) throws Exception {
Object animal1 = createAnimal("Monkey");
// java8 写法,后面如果明确用做精确的类型,需要强制转换
if (animal1 instanceof Monkey) {
System.out.println(animal1);
}
Object animal2 = createAnimal("Cow");
if (animal2 instanceof Cow) {
System.out.println(animal2);
}
// java17 写法,不需要强制转换
if (createAnimal("Monkey") instanceof Monkey animal3) {
System.out.println(animal3);
}
if (createAnimal("Cow") instanceof Cow animal4) {
System.out.println(animal4);
}
}
}
Javascript
Dynamically typed language, use instanceof operator to determine
class Animal {
toString() {
return 'I am an animal';
}
}
class Monkey extends Animal {
toString() {
return 'I am a monkey';
}
}
class Cow extends Animal {
toString() {
return 'I am a cow';
}
}
function createAnimal(animalType) {
switch (animalType) {
case 'Monkey':
return new Monkey();
case 'Cow':
return new Cow();
default:
throw new Error(`Unknown animal type: ${animalType}`);
}
}
try {
const animal1 = createAnimal('Monkey');
if (animal1 instanceof Monkey) {
console.log(animal1.toString());
}
const animal2 = createAnimal('Cow');
if (animal2 instanceof Cow) {
console.log(animal2.toString());
}
const animal3 = createAnimal('Dog');
} catch (error) {
console.error(error.message);
}
Kotlin
Kotlin can use Sealed Class (sealed class) and Any type in two ways. The scenario of using Any is similar to Java returning Object. Sealed Class is safer and more convenient.
Use Any type
open class Animal
class Monkey: Animal() {
override fun toString(): String {
return "I am a monkey!"
}
}
class Cow: Animal() {
override fun toString(): String {
return "I am a cow!"
}
}
fun createAnimal(type: String): Any {
return when (type) {
"Monkey" -> Monkey()
"Cow" -> Cow()
else -> throw IllegalArgumentException("Unknown animal type: $type")
}
}
fun main() {
val animal1 = createAnimal("Monkey")
when (animal1) {
is Monkey -> println(animal1)
is Cow -> println(animal1)
}
val animal2 = createAnimal("Cow")
when (animal2) {
is Monkey -> println(animal2)
is Cow -> println(animal2)
}
}
UseSealedClass
sealed class Animal {
data class Monkey(val info: String = "I am a monkey!") : Animal()
data class Cow(val info: String = "I am a cow!") : Animal()
}
fun createAnimal(type: String): Animal {
return when (type) {
"Monkey" -> Animal.Monkey()
"Cow" -> Animal.Cow()
else -> throw IllegalArgumentException("Unknown animal type: $type")
}
}
fun main() {
val animal1 = createAnimal("Monkey")
when (animal1) {
is Animal.Monkey -> println(animal1.info)
is Animal.Cow -> println(animal1.info)
}
val animal2 = createAnimal("Cow")
when (animal2) {
is Animal.Monkey -> println(animal2.info)
is Animal.Cow -> println(animal2.info)
}
}
Python
Python is a dynamically typed language that can simply return objects of different types based on some conditions, and then use the type() function or isinstance() function to determine its type after receiving the return value.
class Animal:
def __str__(self):
return "I am an animal"
class Monkey(Animal):
def __str__(self):
return "I am a monkey"
class Cow(Animal):
def __str__(self):
return "I am a cow"
def create_animal(animal_type):
if animal_type == "Monkey":
return Monkey()
elif animal_type == "Cow":
return Cow()
else:
raise ValueError(f"Unknown animal type: {animal_type}")
def main():
animal1 = create_animal("Monkey")
if isinstance(animal1, Monkey):
print(animal1)
animal2 = create_animal("Cow")
if isinstance(animal2, Cow):
print(animal2)
if __name__ == "__main__":
main()
Ruby
Ruby is also relatively simple, returning objects of different types directly within the method. You can then use the is_a method or the class method to determine the Actual type.
class Animal
def to_s
"I am an animal"
end
end
class Monkey < Animal
def to_s
"I am a monkey"
end
end
class Cow < Animal
def to_s
"I am a cow"
end
end
def create_animal(animal_type)
case animal_type
when "Monkey"
Monkey.new
when "Cow"
Cow.new
else
raise "Unknown animal type: #{animal_type}"
end
end
begin
animal1 = create_animal("Monkey")
if animal1.is_a? Monkey
puts animal1
end
animal2 = create_animal("Cow")
if animal2.is_a? Cow
puts animal2
end
end
Rust
In Rust, you can use enums to create a data structure that holds multiple different types. Then use the match statement to do pattern matching.
use std::fmt;
enum Animal {
Monkey,
Cow,
}
impl fmt::Display for Animal {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Animal::Monkey => write!(f, "I am a monkey!"),
Animal::Cow => write!(f, "I am a cow!"),
}
}
}
fn create_animal(animal_type: &str) -> Result<Animal, String> {
match animal_type {
"Monkey" => Ok(Animal::Monkey),
"Cow" => Ok(Animal::Cow),
_ => Err(format!("Unknown animal type: {}", animal_type)),
}
}
fn main() {
match create_animal("Monkey") {
Ok(animal) => match animal {
Animal::Monkey => println!("{}", animal),
_ => (),
},
Err(e) => println!("{}", e),
}
match create_animal("Cow") {
Ok(animal) => match animal {
Animal::Cow => println!("{}", animal),
_ => (),
},
Err(e) => println!("{}", e),
}
match create_animal("Dog") {
Ok(_) => (),
Err(e) => println!("{}", e),
}
}
Scala
In Scala, you can use the sealed trait and case class to create a method that can return multiple different types. The Sealed trait can define a limited set of subclasses to ensure type safety
sealed trait Animal {
def info: String
}
case class Monkey() extends Animal {
val info: String = "I am a monkey!"
}
case class Cow() extends Animal {
val info: String = "I am a cow!"
}
object MultiTypeReturnExample {
def createAnimal(animalType: String): Animal = {
animalType match {
case "Monkey" => Monkey()
case "Cow" => Cow()
case _ => throw new IllegalArgumentException(s"Unknown animal type: $animalType")
}
}
def main(args: Array[String]): Unit = {
try {
val animal1 = createAnimal("Monkey")
animal1 match {
case Monkey() => println(animal1.info)
case _ =>
}
val animal2 = createAnimal("Cow")
animal2 match {
case Cow() => println(animal2.info)
case _ =>
}
} catch {
case e: IllegalArgumentException => println(e.getMessage)
}
}
}
TypeScript
Generally speaking, it is not much different from JavaScript
abstract class Animal {
abstract toString(): string;
}
class Monkey extends Animal {
toString(): string {
return 'I am a monkey';
}
}
class Cow extends Animal {
toString(): string {
return 'I am a cow';
}
}
function createAnimal(animalType: string): Animal {
switch (animalType) {
case 'Monkey':
return new Monkey();
case 'Cow':
return new Cow();
default:
throw new Error(`Unknown animal type: ${animalType}`);
}
}
try {
const animal1 = createAnimal('Monkey');
if (animal1 instanceof Monkey) {
console.log(animal1.toString());
}
const animal2 = createAnimal('Cow');
if (animal2 instanceof Cow) {
console.log(animal2.toString());
}
const animal3 = createAnimal('Dog');
} catch (error) {
console.error(error.message);
}
Click to follow and learn about Huawei Cloud’s new technologies as soon as possible~