关键代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
var (
logChannel = make(chan *Entry, 1024*512)
wg sync.WaitGroup
)


// Send log entry to the channel
func sendLog(level logrus.Level, msg string) {

if caller, success := getCaller(3); success {
msg = caller + ":" + msg
}

entry := &Entry{
level: level,
message: msg,
}
logChannel <- entry
}

func init() {

wg.Add(1)
go processLogEntries()
}


func Shutdown() {
close(logChannel)
wg.Wait()
fmt.Println("logging 👋")
}

阅读全文 »

假期打算利用 antlr4 快速入门自制编程语言。采用 antlr4是因为是参考了gscript,同时其可以较实现语法解析的部分。 因为要使用antlr4 所以要了解两点,第一点是 antlr4能做什么,第二点是antlr4是如何用。

阅读全文 »

\ php go java
packTool composer go mod maven
官网 https://getcomposer.org/ https://golang.google.cn/ (语言自带) https://maven.apache.org/
仓库 https://packagist.org/ https://pkg.go.dev/ https://mvnrepository.com/

关于 JAVA go php 中类型推导与推断对比。

首先 php 这种弱类型解释型语言其实本身在使用用过程中不是严格需要类型推导的。比如下面的简单代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
class Cat{
function void say(){
echo 'miaomiao~';
}
}
class Dog{
function void eat(){
echo 'wangwang';
}
}

function void runCat($var){
$var->say();
}

runCat(new Cat());

上面的代码是可以运行且不报错的,那么类型推导的话题原因是从何而来的呢。现在在对比看一下 java 的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Main {
static class Cat{
public void say(){
System.out.println("miaomiao");
}
}
static class Dog{
public void eat(){
System.out.println("wangwang");
}
}

static void runCat(Object obj){
obj.say();
}

public static void main(String[] args) {
runCat(new Cat());
}
}

上述的代码是不不可通过编译的。其实在此处,所提到的类型推导就出现了需要的点。关于java的代码是可以这样处理的。

1
2
3
4
static void runCat(Object obj){
Cat c = (Cat) obj;
c.say();
}

这样处理下 java 的代码就可以正常运行了。

但是上述两个例子传递的内容不是 Cat , 而是 Dog。

其中php报错为方法不存在,java可正常编译,而运行时报org.example.Main$Dog cannot be cast to org.example.Main$Cat

这次话题就正式展开了,首先这种场景的类型推导/转化是因为在代码中使用了一个范围非常大的参数变量 php 中体现为没有加类型限定。java 中体现为使用 object 作为形参。对应到 go 则就是用 any 作为形参。

关于 go 用 any 作为形参时的类型转化有这么几种方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
type Cat struct {
}

func (itself *Cat) Say() {
fmt.Println("miaomiao")
}

func runCat(c any) {
if cEntity, ok := c.(Cat); ok {
cEntity.Say()
}
}

func runCat2(c any) {
switch entity:=c.(type) {
case Cat:
entity.Say()
break
}
}

func runTmp(cmd *cobra.Command, args []string) {
runCat(new(Cat))
}

看到这其实已经有些相似之处了。现在再看下面两份代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
class Cat{
function void say(){
echo 'miaomiao~';
}
}
class Dog{
function void eat(){
echo 'wangwang';
}
}

function void runCat($var){
if($var instanceof Cat){
$var->say();
} else {
throw new Exception(get_class($var).' can\'t cast to Cat');
}
}

runCat(new Cat());
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

public class Main {
static class Cat{
public void say(){
System.out.println("miaomiao");
}
}
static class Dog{
public void eat(){
System.out.println("wangwang");
}
}

static void say(Object obj) throws Exception {
if(obj instanceof Cat){
Cat c = (Cat) obj;
c.say();
} else {
throw new Exception("org.example.Main$Dog cannot be cast to org.example.Main$Cat");
}
}

public static void main(String[] args) throws Exception {
say(new Dog());
}
}

上面的代码显式的展示了类型报错,其实就知道这种情况下的处理方式。首先 php 是运行时报错,所以编码时要提前考虑,如果假如了类型限定需要 phpstan 做检测。而 java 的编译也不会对类型转化做严格的检测,等到运行时开始报错。go 的类型推导相对严格,如果没有处理好,则编译阶段就无法通过。如果真的需要用一个 any/object/param 接收不同类型需要提前做好处理。当然正常的情况下用 object 这种变量传参肯定是不推荐的。但是如果用了且出现多个不同的实参类型,则要考虑周到也就是上述代码中 else 中的处理逻辑,否则运行过程中代码就会把错误扔出来了。这个事情的起因也是因为一次公司内的微服务之间通讯用 object 作为协议参数。但是传参方更改了实参。接收方只做了一种类型的强制转化,没有做类型推导判断,最终出现了报错,从源头上来说是服务方采用了不合理的协议设计。但是实及表现上来说 java 这种 object 类型转化更类似于类型断言,如果需要使用的话,还是需要利用 instanceof 来对类型进行真正的检测,从而对类型进行周全的处理。

之前用 go 的泛型,写了相关的 collection 操作函数。所以又了对 fiber gin 这类 http lib 升级的想法,使得控制器的编写更加方便。

这里放一个最基础的例子,更多的编写放在了后方。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ginUpP  支持params 参数
func ginUpP[T any](action func(request T) component.Response) func(c *gin.Context) {
return func(c *gin.Context) {
var params T
_ = c.ShouldBind(&params)
err := validate.Struct(params)
if err != nil {
c.JSON(http.StatusBadRequest, component.DataMap{
"msg": err.Error(),
})
}
response := action(params)
c.JSON(response.Code, response.Data)
}
}

这段代码可以使得类似这样的控制器代码很方便的撰写,将大量的重复代码剥离出控制器外。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
type GetTwitterUserListParam struct {
Page int `form:"page"`
PageSize int `form:"pageSize"`
Search string `form:"search"`
}

func GetTwitterUserList(param GetTwitterUserListParam) component.Response {
pageData := FTwitterUser.Page(FTwitterUser.PageQuery{
Page: param.Page, PageSize: param.PageSize, Search: param.Search,
})

return component.SuccessResponse(component.DataMap{
"itemList": arms.ArrayMap(func(item FTwitterUser.FTwitterUser) TLink {
return TLink{
ScreenName: item.ScreenName,
Name: item.Name,
Desc: item.Desc,
Url: fmt.Sprintf("https://twitter.com/%v/with_replies", item.ScreenName),
CreateTime: item.CreateTime.Format("2006-01-02 15:05:05"),
}
}, pageData.Data),
"size": pageData.PageSize,
"total": pageData.Total,
"current": param.Page,
})
}

唯一的区别就是路由注册的代码可能会稍微长一点点

1
apiGroup.GET("/GetTwitterUserList", ginUpP(controllers.GetTwitterUserList))

阅读全文 »
0%